目录
摘要
在C++中,`new` 和 `delete` 运算符用于动态内存分配和释放,对它们的使用将会直接影响到程序的内存管理和性能优化。
常规用法
`new` 运算符
`new` 运算符在堆上分配内存,并调用对象的构造函数。它返回一个指向已分配内存的指针。
分配单个对象
#include <iostream>
int main() {
int* p = new int; // 分配一个整数
*p = 5; // 给分配的整数赋值
std::cout << "Value: " << *p << std::endl; // 输出整数值
delete p; // 释放内存
return 0;
}
分配数组
#include <iostream>
int main() {
int* arr = new int[10]; // 分配一个整数数组
for (int i = 0; i < 10; ++i) {
arr[i] = i; // 给数组元素赋值
}
for (int i = 0; i < 10; ++i) {
std::cout << arr[i] << " "; // 输出数组元素
}
std::cout << std::endl;
delete[] arr; // 释放数组内存
return 0;
}
`delete` 运算符
`delete` 运算符用于释放由 `new` 分配的内存,并调用对象的析构函数。
释放单个对象
#include <iostream>
int main() {
int* p = new int(10); // 分配并初始化一个整数
std::cout << "Value: " << *p << std::endl; // 输出整数值
delete p; // 释放内存
return 0;
}
释放数组
#include <iostream>
int main() {
int* arr = new int[10]; // 分配一个整数数组
for (int i = 0; i < 10; ++i) {
arr[i] = i;
}
delete[] arr; // 释放数组内存
return 0;
}
高级用法
自定义 `new` 和 `delete` 运算符
在C++中,可以自定义 `new` 和 `delete` 运算符,以实现特定的内存管理需求。
自定义全局 `new` 和 `delete` 运算符
#include <iostream>
#include <cstdlib> // 使用 malloc 和 free
// 重载全局 new 运算符
void* operator new(std::size_t size) {
std::cout << "Custom new for size: " << size << std::endl;
void* p = std::malloc(size);
if (!p) throw std::bad_alloc();
return p;
}
// 重载全局 delete 运算符
void operator delete(void* p) noexcept {
std::cout << "Custom delete" << std::endl;
std::free(p);
}
int main() {
int* p = new int(10); // 将使用自定义的 new 运算符
std::cout << "Value: " << *p << std::endl;
delete p; // 将使用自定义的 delete 运算符
return 0;
}
自定义类级别的 `new` 和 `delete` 运算符
#include <iostream>
#include <cstdlib> // 使用 malloc 和 free
class MyClass {
public:
int value;
MyClass(int v) : value(v) {}
// 重载类级别的 new 运算符
void* operator new(std::size_t size) {
std::cout << "Custom class-level new for size: " << size << std::endl;
void* p = std::malloc(size);
if (!p) throw std::bad_alloc();
return p;
}
// 重载类级别的 delete 运算符
void operator delete(void* p) noexcept {
std::cout << "Custom class-level delete" << std::endl;
std::free(p);
}
};
int main() {
MyClass* obj = new MyClass(42); // 使用自定义的类级别 new 运算符
std::cout << "Value: " << obj->value << std::endl;
delete obj; // 使用自定义的类级别 delete 运算符
return 0;
}
定位 `new` 运算符
定位 `new` 运算符允许在指定的内存地址上构造对象。这对于需要在预分配的内存块上构造对象的情况特别有用。
定位 `new`
#include <iostream>
#include <new> // 使用定位 new
struct MyStruct {
int x, y;
MyStruct(int a, int b) : x(a), y(b) {
std::cout << "MyStruct constructed" << std::endl;
}
~MyStruct() {
std::cout << "MyStruct destructed" << std::endl;
}
};
int main() {
char buffer[sizeof(MyStruct)]; // 预分配内存
// 在预分配的内存上构造对象
MyStruct* p = new (buffer) MyStruct(1, 2);
std::cout << "p->x: " << p->x << ", p->y: " << p->y << std::endl;
// 手动调用析构函数
p->~MyStruct();
return 0;
}
`new` 和 `delete` 与C++内核的关系
`new` 和 `delete` 运算符与C++内核(runtime library)有着紧密的联系。C++内核库负责管理内存的分配和释放。具体来说,标准库提供了 `malloc` 和 `free` 函数来实现低级别的内存管理,`new` 和 `delete` 运算符在其基础上进一步封装,实现了对象的构造和析构。
内存分配过程
1. new运算符:
- 调用操作系统的内存分配函数(通常是 `malloc`)。
- 分配成功后,调用对象的构造函数。
- 返回指向新分配和初始化的对象的指针。
2. delete运算符:
- 调用对象的析构函数。
- 调用操作系统的内存释放函数(通常是 `free`)。
关键点
1. 异常安全:如果在构造函数中抛出异常,`new` 运算符会自动释放已分配的内存,以防止内存泄漏。
2. 全局重载:通过全局重载 `new` 和 `delete` 运算符,可以自定义内存分配策略,例如内存池、垃圾回收等。
3. 局部重载:类级别的 `new` 和 `delete` 运算符允许为特定类自定义内存管理。
异常安全
#include <iostream>
#include <new> // 使用 std::bad_alloc
class MyClass {
public:
MyClass() {
std::cout << "Constructor" << std::endl;
throw std::runtime_error("Exception in constructor");
}
~MyClass() {
std::cout << "Destructor" << std::endl;
}
};
int main() {
try {
MyClass* obj = new MyClass(); // 构造函数中抛出异常
delete obj;
} catch (const std::exception& e) {
std::cout << "Caught exception: " << e.what() << std::endl;
}
return 0;
}
注意事项
1. 内存泄漏:每个 `new` 必须对应一个 `delete`,否则会导致内存泄漏。
2. 数组分配和释放:分配数组时使用 `new[]`,释放数组时使用 `delete[]`,不要混用。
3. 智能指针:C++11 引入了智能指针(如 `std::unique_ptr` 和 `std::shared_ptr`),可以自动管理内存,减少手动调用 `new` 和 `delete` 的需要。
使用智能指针
#include <iostream>
#include <memory> // 使用智能指针
int main() {
std::unique_ptr<int> p = std::make_unique<int>(10); // 自动管理内存
std::cout << "Value: " << *p << std::endl;
// 内存会在智能指针析构时自动释放
return 0;
}