ARTICLE AD BOX
The provided code is not portable, because a std::atomic<int64_t> is not trivially-constructible on all platforms, and malloc() might not align the memory it returns as strictly as it needs.
A supposedly-standardized way to do this safely is to allocate the aligned storage with aligned_alloc() and create the object in aligned storage with placement new. This should get an object that (if atomic_int64_t is trivially-destructible) can be deallocated with free(). After the necessary #include and using directives, this might look like:
atomic_int64_t* mustFree = new(aligned_alloc(alignof(atomic_int64_t), sizeof(atomic_int64_t))) atomic_int64_t{1}; if (!mustFree) { throw std::bad_alloc{}; }But this does not work on Microsoft Visual C++, which does not support aligned_alloc(). On that platform, std::atomic_int64_t should be always-lock-free and naturally-aligned, so malloc() should work for it.
Although you don’t say what the purpose is, I only call malloc() or free() from C++ code that needs to work with a library written in C. I always prefer to write RIIA smart-pointer wrappers for that. Since you can give std::unique_ptr a custom deallocator, there is already a class in the standard library that you can overload to call free() automatically. You can do this out of the box with:
auto up = std::unique_ptr<long long int, void(*)(void*)>( static_cast<long long int*>(ptr_from_malloc_or_calloc), (std::free));This also works to wrap something from a C library that has some kind of foolib_make_foo() and foolib_free_foo(). But if you’re willing to include some boilerplate, you can get nicer syntax and zero overhead:
/* Helper class to destroy an object created in uninitialized storage that * was obtained from malloc(), calloc() or aligned_alloc(). */ template<class T> class CDeleter { public: static void operator() (T* const p) noexcept(std::is_nothrow_destructible_v<T>) { if (p) { p->~T(); } free(p); } }; /* Allocate a smart pointer that will call the owned object's destructor, followed * by free(), or throw std::bad_alloc on allocation failure. The object is created * in place, with the function arguments perfectly forwarded to its constructor. */ template<class T, class... Args> requires std::constructible_from<T, Args...> std::unique_ptr<T, CDeleter<T> > make_C_dynamic_up(Args&&... args) { void* mem = nullptr; if constexpr (alignof(T) <= alignof(std::max_align_t)) { mem = calloc(1, sizeof(T)); } else { #ifndef _MSC_VER /* Use aligned_alloc() on Standard-conforming compilers (not Microsoft's). */ mem = std::aligned_alloc(alignof(T), sizeof(T)); #else /* Since the workaround on MSVC is _aligned_malloc(), which is incompatible * with free(), cause a compiler error instead. */ static_assert(false, "Type alignment is too restrictive for malloc()"); #endif } // end if constexpr if (!mem) { throw std::bad_alloc{}; } return std::unique_ptr<T, CDeleter<T> >{ new(mem) T(std::forward<Args>(args)...)}; } template <class T> using CDynUP = std::unique_ptr<T, CDeleter<T> >;This lets you then write, similarly to std::make_unique,
const auto up = make_C_dynamic_up<atomic_int64_t>(1);which allocates storage for an atomic 64-bit integer using calloc(), initializes the owned variable to 1, and returns a smart pointer to it.
If you need to wrap a pointer you received from a C API, you can do that with,
// The returned pointer must be freed with free(): extern "C" atomic_int64_t* some_extern_factory_func(void); CDynUP<atomic_int64_t> up{ some_extern_factory_func() };If you need to pass this pointer to C code that will free it, pass up.release(). APIs that don’t need to take ownership should be passed *up or up.get().
