C++ new vs malloc | Constructor·Type Safety
이 글의 핵심
C++ new vs malloc differences. Constructor·destructor, type safety, exception vs nullptr on failure. Performance is almost the same but why new·delete is correct for C++ objects and practical selection.
Introduction
C++ provides two memory allocation methods: new and malloc. malloc is a function inherited from C, and new is a C++-specific operator. To use an analogy, malloc is like just opening an empty room, and new is like handing over a room with furniture assembled. Since C++ objects have constructors·destructors, new/delete is correct for matching initialization not just “a room”.
After Reading This
- Understand 7 differences between new and malloc
- Grasp differences in constructor calling, type safety, exception handling
- Learn performance comparison and practical selection criteria
- Check precautions when mixing and C library integration patterns
Table of Contents
- new vs malloc 7 Differences
- Practical Implementation
- Advanced Usage
- Performance Comparison
- Practical Cases
- Troubleshooting
- Conclusion
new vs malloc 7 Differences
Comparison Table
| Item | new | malloc |
|---|---|---|
| Language | C++ operator | C function |
| Constructor call | ✅ Calls | ❌ Does not call |
| Type safety | ✅ Safe (no casting needed) | ❌ Unsafe (casting needed) |
| Size calculation | Automatic | Manual (sizeof) |
| On failure | Exception (bad_alloc) | Returns nullptr |
| Deallocation | delete | free |
| Array allocation | new[] | malloc + size |
| Overload | Possible (operator new) | Impossible |
Practical Implementation
1) Basic Type Allocation
malloc
#include <cstdlib>
#include <iostream>
int main() {
// malloc: casting needed
int* ptr = (int*)malloc(sizeof(int));
if (ptr == nullptr) { // nullptr check needed
std::cerr << "Allocation failed" << std::endl;
return 1;
}
*ptr = 42;
std::cout << *ptr << std::endl;
free(ptr);
return 0;
}
new
#include <iostream>
int main() {
// new: no casting needed, initialization simultaneous
int* ptr = new int(42);
std::cout << *ptr << std::endl;
delete ptr;
return 0;
}
2) Class Allocation
#include <iostream>
class MyClass {
private:
int x_;
public:
MyClass() : x_(0) {
std::cout << "Constructor called" << std::endl;
}
~MyClass() {
std::cout << "Destructor called" << std::endl;
}
void setValue(int x) { x_ = x; }
int getValue() const { return x_; }
};
int main() {
// ❌ malloc: constructor not called
MyClass* obj1 = (MyClass*)malloc(sizeof(MyClass));
// "Constructor called" not printed
// obj1->x_ is garbage value
free(obj1); // destructor not called
// ✅ new: constructor called
MyClass* obj2 = new MyClass();
// "Constructor called" printed
obj2->setValue(42);
std::cout << obj2->getValue() << std::endl;
delete obj2; // "Destructor called" printed
return 0;
}
Important: Class objects must use new.
3) Array Allocation
malloc
// malloc: manual size calculation
int* arr1 = (int*)malloc(10 * sizeof(int));
for (int i = 0; i < 10; ++i) {
arr1[i] = i;
}
free(arr1);
new
// new: automatic size calculation
int* arr2 = new int[10];
for (int i = 0; i < 10; ++i) {
arr2[i] = i;
}
delete[] arr2; // delete[] for arrays
4) Exception Handling
malloc: Returns nullptr
#include <cstdlib>
#include <iostream>
int main() {
int* ptr = (int*)malloc(1000000000000); // Allocate 1TB (fails)
if (ptr == nullptr) { // Manual check needed
std::cerr << "Allocation failed" << std::endl;
return 1;
}
free(ptr);
return 0;
}
new: Throws exception
#include <iostream>
int main() {
try {
int* ptr = new int[1000000000000]; // Allocate 1TB (fails)
delete[] ptr;
} catch (const std::bad_alloc& e) {
std::cerr << "Allocation failed: " << e.what() << std::endl;
}
return 0;
}
new (std::nothrow)
#include <new>
#include <iostream>
int main() {
int* ptr = new (std::nothrow) int[1000000000000];
if (ptr == nullptr) {
std::cerr << "Allocation failed" << std::endl;
return 1;
}
delete[] ptr;
return 0;
}
Advanced Usage
Placement new
alignas(MyType) unsigned char buf[sizeof(MyType)];
MyType* obj = new (buf) MyType(42); // construct in existing storage, no allocation
obj->~MyType(); // must call the destructor manually — no delete
malloc has no equivalent: it only ever allocates raw storage, it never runs a constructor, so there’s nothing to “place into” existing memory the way placement new does.
nothrow new
MyType* p = new (std::nothrow) MyType();
if (!p) { /* handle allocation failure without a try/catch */ }
Closer to malloc’s failure model (NULL on failure) than ordinary new, which throws std::bad_alloc.
Overloading operator new/delete
void* operator new(std::size_t size) {
void* p = std::malloc(size);
if (!p) throw std::bad_alloc();
return p;
}
void operator delete(void* p) noexcept { std::free(p); }
This is how custom allocators, memory pools, and allocation-tracking tools hook into every new in a program — malloc/free have no comparable per-type or global override mechanism in standard C.
Over-aligned allocation (C++17)
struct alignas(64) CacheLineAligned { int data[16]; };
auto* p = new CacheLineAligned(); // uses the C++17 aligned new overload automatically
malloc only guarantees alignment suitable for any built-in type (alignof(std::max_align_t)); for over-aligned types you’d need std::aligned_alloc (C11/C++17) and to remember the matching std::free.
Performance Comparison
Benchmark Results
// Benchmark: 1 million allocations
// new: 125ms
// malloc: 120ms
// Difference: ~4% (negligible)
Conclusion: Performance difference is almost none. new internally calls malloc then runs constructor.
Practical Cases
Case 1: C++ Object
// ✅ Correct: new
class MyClass {
public:
MyClass() { /* initialization */ }
~MyClass() { /* cleanup */ }
};
MyClass* obj = new MyClass();
delete obj;
Case 2: POD Type
// Both OK, but new is safer
int* ptr1 = new int(42);
int* ptr2 = (int*)malloc(sizeof(int));
delete ptr1;
free(ptr2);
Case 3: C Library Integration
// C library uses malloc
void* ptr = some_c_function(); // Returns malloc pointer
free(ptr); // Must use free
// Wrap with smart pointer
std::unique_ptr<void, decltype(&free)> smart_ptr(ptr, free);
Troubleshooting
“mixing new/delete with malloc/free” crashes or corrupts the heap
Cause: new/delete and malloc/free are not required to share the same allocator internally — pairing new with free(), or malloc() with delete, is undefined behavior even when it happens to “work” in a given build.
Fix: always pair new↔delete, new[]↔delete[], and malloc/calloc/realloc↔free. When wrapping a C API’s malloc’d pointer in a smart pointer (Case 3 above), pass free as the deleter explicitly rather than letting unique_ptr’s default deleter call delete on it.
Destructor doesn’t run
Cause: allocating a class type with malloc (or calloc) skips the constructor and the eventual free() skips the destructor — any RAII members (a std::string, a std::vector, another smart pointer) leak or never get cleaned up.
Fix: never malloc a non-trivial C++ type. If you must interoperate with a C allocator, use placement new on malloc’d storage and call the destructor manually before freeing it (see Advanced Usage above) — or, simpler, just use new/delete.
realloc on a new-allocated buffer
Cause: realloc assumes the block came from malloc’s allocator; calling it on memory from new[] is undefined behavior, and even where it happens not to crash, it never runs move/copy constructors for the elements the way std::vector’s growth does.
Fix: use std::vector (or std::realloc only on malloc’d buffers) instead of trying to realloc a new[] array.
Summary
Key Points
- new: C++ operator, calls constructor, type-safe
- malloc: C function, no constructor, manual casting
- Performance: Almost same (new calls malloc internally)
- Recommendation: Use new for C++ objects
- Smart pointers: Modern C++ best practice
Decision Flowchart
C++ object?
├─ Yes → new/delete (or smart pointer)
└─ No (POD type)
└─ C library integration?
├─ Yes → malloc/free
└─ No → new/delete (safer)
Best Practices
- ✅ Use new for C++ objects
- ✅ Use smart pointers in modern C++
- ✅ Pair malloc-free, new-delete
- ❌ Don’t mix malloc-delete or new-free
- ❌ Don’t use raw pointers in modern C++
Related Articles
- C++ Memory Basics
- C++ Smart Pointers
- C++ malloc vs new vs make_unique Master C++ memory management! 🚀
Frequently Asked Questions (FAQ)
Q. When would I use this in practice?
A. C++ new vs malloc differences. Constructor·destructor, type safety, exception vs nullptr on failure.
Q. What should I read before this?
A. Follow the previous article or related articles links at the bottom of each post to learn in sequence. See the C++ series index for the full picture.
Q. Where can I study this more deeply?
A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.
Related Articles (Internal Links)
Other articles related to this topic.
- C++ 스택 vs 힙 | 재귀에서 프로그램이 죽는 이유와 스택 오버플로우 사례
- C++ 스마트 포인터 | unique_ptr/shared_ptr ‘메모리 안전’ 가이드
- C++ malloc vs new vs make_unique | 메모리 할당 완벽 비교
Keywords Covered in This Article (Related Search Terms)
This article covers C++, new, malloc, memory-allocation, dynamic-allocation, constructor, type-safety.