C++内存管理

之前在别人的面经中看到了有面试官会问:new operator, operator newplacement new的区别。(根本没在学习C++的时候了解过这个东西)所以特此记录一下。

C++三种内存管理的基本案例

1. new operator (new操作符)

new operator做了两件事:

  1. 调用operator new分配内存;
  2. 在分配的内存上调用构造函数。
1
2
// 这是我们最常用的new,它是C++的关键字
MyClass* obj = new MyClass(args); // new operator

2. operator new (new操作符函数)

operator new等价于malloc,只分配了内存不会调用构造函数。

1
2
3
4
5
// 这是一个可以重载的函数,只负责分配内存
void* ptr = operator new(sizeof(MyClass)); // 只分配内存,不调用构造函数

// 等价于malloc,但可以重载
void* ptr = malloc(sizeof(MyClass)); // C风格的内存分配

3. placement new (定位new)

1
2
3
4
5
6
7
// 在已分配的内存上构造对象
char buffer[sizeof(MyClass)];
MyClass* obj = new(buffer) MyClass(args); // placement new

// 或者
void* memory = operator new(sizeof(MyClass));
MyClass* obj = new(memory) MyClass(args); // 在指定内存位置构造对象

所以,如果想要使用这个对象。一般直接new operator。要么就是operator new + placement new这个组合来实例化对象。

🔍 详细分析和示例

1. new operator - 完整的对象创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>

class MyClass {
int value;
public:
MyClass(int v) : value(v) {
std::cout << "Constructor called, value = " << value << std::endl;
}

~MyClass() {
std::cout << "Destructor called, value = " << value << std::endl;
}

void print() const {
std::cout << "Value: " << value << std::endl;
}
};

void demo_new_operator() {
std::cout << "=== new operator demo ===" << std::endl;

// new operator: 分配内存 + 调用构造函数
MyClass* obj = new MyClass(42);
obj->print();

// delete operator: 调用析构函数 + 释放内存
delete obj;
}

new operator的内部过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
// new MyClass(42) 等价于:
MyClass* obj;
try {
// 1. 分配内存
void* memory = operator new(sizeof(MyClass));

// 2. 在内存上构造对象
obj = new(memory) MyClass(42); // placement new
} catch(...) {
// 如果构造函数抛异常,自动释放内存
operator delete(memory);
throw;
}

2. operator new - 纯内存分配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <new>
#include <iostream>

void demo_operator_new() {
std::cout << "=== operator new demo ===" << std::endl;

// 只分配内存,不调用构造函数
void* raw_memory = operator new(sizeof(MyClass));
std::cout << "Memory allocated at: " << raw_memory << std::endl;

// 此时内存中是垃圾数据,不能直接使用
// MyClass* obj = static_cast<MyClass*>(raw_memory); // 危险!
// obj->print(); // 未定义行为

// 需要手动构造对象
MyClass* obj = new(raw_memory) MyClass(100); // placement new
obj->print();

// 手动析构和释放
obj->~MyClass(); // 手动调用析构函数
operator delete(raw_memory); // 释放内存
}

自定义operator new:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class CustomMemoryClass {
int data;

public:
CustomMemoryClass(int d) : data(d) {
std::cout << "Constructor: " << data << std::endl;
}

~CustomMemoryClass() {
std::cout << "Destructor: " << data << std::endl;
}

// 重载operator new
static void* operator new(size_t size) {
std::cout << "Custom operator new called, size = " << size << std::endl;
void* ptr = malloc(size);
if (!ptr) throw std::bad_alloc();
return ptr;
}

// 重载operator delete
static void operator delete(void* ptr) {
std::cout << "Custom operator delete called" << std::endl;
free(ptr);
}

void print() const {
std::cout << "Data: " << data << std::endl;
}
};

void demo_custom_operator_new() {
std::cout << "=== Custom operator new demo ===" << std::endl;

// 使用自定义的内存分配
CustomMemoryClass* obj = new CustomMemoryClass(200);
obj->print();
delete obj;
}

3. placement new - 在指定位置构造对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <new>
#include <memory>

void demo_placement_new() {
std::cout << "=== Placement new demo ===" << std::endl;

// 方式1: 在栈上的buffer中构造对象
alignas(MyClass) char buffer[sizeof(MyClass)];
MyClass* obj1 = new(buffer) MyClass(300);
obj1->print();
obj1->~MyClass(); // 手动析构,不能delete

// 方式2: 在堆上预分配的内存中构造对象
void* memory = operator new(sizeof(MyClass));
MyClass* obj2 = new(memory) MyClass(400);
obj2->print();
obj2->~MyClass();
operator delete(memory);

// 方式3: 在数组中构造多个对象
const int count = 3;
alignas(MyClass) char array_buffer[sizeof(MyClass) * count];

MyClass* objects = reinterpret_cast<MyClass*>(array_buffer);
for (int i = 0; i < count; ++i) {
new(objects + i) MyClass(500 + i); // placement new
}

for (int i = 0; i < count; ++i) {
objects[i].print();
}

// 手动析构所有对象
for (int i = count - 1; i >= 0; --i) {
objects[i].~MyClass();
}
}

🎯 实际使用场景

1. 内存池实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
template<typename T, size_t PoolSize>
class MemoryPool {
alignas(T) char pool[sizeof(T) * PoolSize];
bool used[PoolSize];

public:
MemoryPool() {
std::fill(used, used + PoolSize, false);
}

template<typename... Args>
T* construct(Args&&... args) {
// 找到空闲位置
for (size_t i = 0; i < PoolSize; ++i) {
if (!used[i]) {
used[i] = true;
// 使用placement new在预分配的内存上构造对象
return new(pool + i * sizeof(T)) T(std::forward<Args>(args)...);
}
}
throw std::bad_alloc();
}

void destroy(T* obj) {
// 计算对象在池中的索引
size_t index = (reinterpret_cast<char*>(obj) - pool) / sizeof(T);
if (index < PoolSize && used[index]) {
obj->~T(); // 手动析构
used[index] = false;
}
}
};

void demo_memory_pool() {
std::cout << "=== Memory Pool demo ===" << std::endl;

MemoryPool<MyClass, 10> pool;

// 在内存池中构造对象
MyClass* obj1 = pool.construct(600);
MyClass* obj2 = pool.construct(700);

obj1->print();
obj2->print();

// 销毁对象
pool.destroy(obj1);
pool.destroy(obj2);
}

2. 自定义容器实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
template<typename T>
class SimpleVector {
T* data;
size_t size_;
size_t capacity_;

public:
SimpleVector() : data(nullptr), size_(0), capacity_(0) {}

~SimpleVector() {
clear();
operator delete(data);
}

void push_back(const T& value) {
if (size_ >= capacity_) {
reserve(capacity_ == 0 ? 1 : capacity_ * 2);
}

// 使用placement new在预分配的内存上构造对象
new(data + size_) T(value);
++size_;
}

void reserve(size_t new_capacity) {
if (new_capacity <= capacity_) return;

// 分配新内存
T* new_data = static_cast<T*>(operator new(sizeof(T) * new_capacity));

// 移动现有对象到新内存
for (size_t i = 0; i < size_; ++i) {
new(new_data + i) T(std::move(data[i])); // placement new + move
data[i].~T(); // 析构原对象
}

// 释放旧内存
operator delete(data);

data = new_data;
capacity_ = new_capacity;
}

void clear() {
for (size_t i = 0; i < size_; ++i) {
data[i].~T(); // 手动析构
}
size_ = 0;
}

T& operator[](size_t index) { return data[index]; }
const T& operator[](size_t index) const { return data[index]; }
size_t size() const { return size_; }
};

void demo_custom_vector() {
std::cout << "=== Custom Vector demo ===" << std::endl;

SimpleVector<MyClass> vec;
vec.push_back(MyClass(800));
vec.push_back(MyClass(900));

for (size_t i = 0; i < vec.size(); ++i) {
vec[i].print();
}
}

3. RAII和异常安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
template<typename T>
class SafeConstructor {
void* memory;
T* object;
bool constructed;

public:
template<typename... Args>
SafeConstructor(Args&&... args) : memory(nullptr), object(nullptr), constructed(false) {
try {
// 分配内存
memory = operator new(sizeof(T));

// 构造对象
object = new(memory) T(std::forward<Args>(args)...);
constructed = true;

} catch (...) {
// 异常安全:如果构造失败,释放已分配的内存
if (memory) {
operator delete(memory);
}
throw;
}
}

~SafeConstructor() {
if (constructed && object) {
object->~T();
}
if (memory) {
operator delete(memory);
}
}

T* get() { return constructed ? object : nullptr; }
T* release() {
T* result = object;
object = nullptr;
constructed = false;
return result;
}
};

📊 三者对比总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void comparison_demo() {
std::cout << "=== Comparison Demo ===" << std::endl;

// 1. new operator - 最常用,完整的对象创建
MyClass* obj1 = new MyClass(1); // 分配内存 + 构造对象
delete obj1; // 析构对象 + 释放内存

// 2. operator new - 只分配内存
void* memory = operator new(sizeof(MyClass)); // 只分配内存
MyClass* obj2 = new(memory) MyClass(2); // placement new构造对象
obj2->~MyClass(); // 手动析构
operator delete(memory); // 释放内存

// 3. placement new - 在指定位置构造对象
alignas(MyClass) char buffer[sizeof(MyClass)];
MyClass* obj3 = new(buffer) MyClass(3); // 在栈buffer上构造
obj3->~MyClass(); // 手动析构,不释放内存
}

🎯 使用场景总结

方式 使用场景 优势 注意事项
new operator 日常对象创建 简单安全,自动管理 性能开销,内存碎片
operator new 自定义内存管理 灵活控制分配策略 需要手动构造对象
placement new 内存池、容器、嵌入式 零分配开销,精确控制 需要手动析构

最佳实践:

  1. 一般情况:使用new operator和智能指针
  2. 性能关键:考虑内存池 + placement new
  3. 自定义容器operator new + placement new组合
  4. 嵌入式系统placement new避免动态分配

这三种方式各有用途。在上层应用级的开发中,直接使用new operator最直接。但是如果想要更细粒度地去分配、管理内存资源,减少一些额外的内存管理开销,就要手动的operator new + placement new两种方法了。