相关系列文章
C++ new 和 malloc 的区别?
C++内存分配策略
目录
1.引言
2.区别
2.1.申请的内存分配区域
2.2.类型安全和自动大小计算
2.3.构造函数和析构函数的调用
2.4.异常处理
2.5.配对简便性
2.6.new 的重载
2.7.关键字和操作符
3.总结
1.引言
new
和 delete
在 C++ 中被引入,主要是为了解决 malloc
和 free
在 C 语言中的一些限制和问题,特别是在面向对象编程方面。以下是 new/delete
相比于 malloc/free
的主要区别。
2.区别
2.1.申请的内存分配区域
new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。
那么自由存储区是否能够是堆(问题等价于new是否能在堆上动态分配内存),这取决于operator new 的实现细节。自由存储区不仅可以是堆,还可以是静态存储区,这都看operator new在哪里为对象分配内存。
特别的,new甚至可以不为对象分配内存!定位new的功能可以办到这一点:
new (place_address) type
place_address为一个指针,代表一块内存的地址。当使用上面这种仅以一个地址调用new操作符时,new操作符调用特殊的operator new,也就是下面这个版本:
void * operator new (size_t,void *)
//不允许重定义这个版本的operator new
这个operator new不分配任何的内存,它只是简单地返回指针实参,然后右new表达式负责在place_address指定的地址进行对象的初始化工作。
2.2.类型安全和自动大小计算
malloc/free 示例:
#include <stdlib.h>
struct MyStruct {
int data;
// ... 其他成员 ...
};
int main() {
// 使用 malloc 分配内存,需要手动计算大小
MyStruct* p = (MyStruct*)malloc(sizeof(MyStruct));
p->data = 10;
free(p);
return 0;
}
new/delete 示例:
struct MyStruct {
int data;
// ... 其他成员 ...
};
int main() {
// 使用 new 分配内存,自动处理大小和类型
MyStruct* p = new MyStruct;
p->data = 10;
delete p;
return 0;
}
区别:
-
new
自动计算所需内存的大小,而malloc
需要程序员手动计算。 -
new
提供类型安全,返回正确类型的指针,避免了强制类型转换的需要。
2.3.构造函数和析构函数的调用
使用new操作符来分配对象内存时会经历三个步骤:
1)调用operator new 函数(对于数组是operator new[])分配一块足够大的,原始的,未命名的内存空间以便存储特定类型的对象。
2)编译器运行相应的构造函数以构造对象,并为其传入初值。
3)对象构造完成后,返回一个指向该对象的指针。
使用delete操作符来释放对象内存时会经历两个步骤:
1)调用对象的析构函数。
2)编译器调用operator delete(或operator delete[])函数释放内存空间。
总之来说,new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构。而malloc则不会。下面看个示例:
class MyClass {
public:
MyClass() { std::cout << "Constructor called\n"; }
~MyClass() { std::cout << "Destructor called\n"; }
};
int main() {
MyClass* obj = new MyClass; // 调用构造函数
delete obj; // 调用析构函数
return 0;
}
区别:
-
new
在分配内存时调用对象的构造函数,delete
在释放内存时调用析构函数。 -
malloc
和free
只处理内存分配和释放,不调用构造函数和析构函数。
2.4.异常处理
new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。示例如下:
int main() {
try {
int* p = new int[10000000000]; // 尝试分配大量内存
} catch (const std::bad_alloc& e) {
std::cerr << "Memory allocation failed: " << e.what() << '\n';
}
return 0;
}
区别:
-
new
在内存分配失败时抛出异常(如std::bad_alloc
),而malloc
在失败时返回NULL
。 -
这使得
new
能够更好地集成到 C++ 的异常处理框架中。
2.5.配对简便性
-
new
和delete
是为对象配对的,而malloc
和free
需要显式计算大小。
如:
//[1]
A * ptr = new A[10];//分配10个A对象
delete []ptr;
//[2]
int * ptr = (int *) malloc( sizeof(int)* 10 );
//分配一个10个int元素的数组
free(ptr);
-
new[]
和delete[]
用于数组,简化了数组内存管理。
new对数组的支持体现在它会分别调用构造函数函数初始化每一个数组元素,释放对象时为每个对象调用析构函数。注意delete[]要与new[]配套使用,不然会找出数组对象部分释放的现象,造成内存泄漏
2.6.new
的重载
是的,C++ 允许重载 new
操作符。这意味着你可以定义自己的 new
操作符来改变对象的分配方式。重载 new
可以用于自定义内存管理,追踪内存分配,或者引入特殊的内存分配策略。标准库是定义了operator new函数和operator delete函数的8个重载版本:
//这些版本可能抛出异常
void * operator new(size_t);
void * operator new[](size_t);
void * operator delete (void * )noexcept;
void * operator delete[](void *0)noexcept;
//这些版本承诺不抛出异常
void * operator new(size_t ,nothrow_t&) noexcept;
void * operator new[](size_t, nothrow_t& );
void * operator delete (void *,nothrow_t& )noexcept;
void * operator delete[](void *0,nothrow_t& )noexcept;
我们可以自定义上面函数版本中的任意一个,前提是自定义版本必须位于全局作用域或者类作用域中。
重载 new
需要提供与系统 new
相同的返回类型和参数列表。最常见的形式是重载全局 new
和 delete
:
void* operator new(std::size_t size) {
std::cout << "Custom new for size " << size << std::endl;
return std::malloc(size);
}
void operator delete(void* memory) {
std::cout << "Custom delete" << std::endl;
std::free(memory);
}
类也可以重载其自身的 new
和 delete
,这对于控制特定类的对象分配非常有用。
class MyClass {
public:
void* operator new(std::size_t size) {
std::cout << "MyClass new" << std::endl;
return std::malloc(size);
}
void operator delete(void* memory) {
std::cout << "MyClass delete" << std::endl;
std::free(memory);
}
};
2.7.关键字和操作符
new 是操作符,malloc 是函数。
关键字(Keywords)和操作符(Operators)在编程语言中是两个不同的概念:
-
关键字:这些是编程语言预定义的保留字,每个关键字有特定的含义,并在语言的语法中扮演特定的角色。例如,
if
、while
、return
等在 C++ 中都是关键字。关键字不能用作变量名或函数名。 -
操作符:操作符用于执行操作,如算术运算、逻辑运算、比较等。在 C++ 中,一些操作符可以被重载,这意味着你可以改变它们的行为以适应特定类型的操作。例如,
+
、-
、*
、/
、new
等都是操作符。
有些情况下,某些关键字也可以被视为操作符。例如,new
和 delete
在 C++ 中既是关键字也是操作符。它们作为关键字,表示特定的动作(分配和释放内存),同时它们的行为可以像操作符那样被重载。
3.总结
new/delete
提供了更符合 C++ 面向对象特性的内存管理方式。它们处理类型安全、对象生命周期(构造和析构)、异常安全以及简化语法。然而,这些改进也带来了一定的性能开销,这在某些性能敏感的应用中可能是一个考虑因素。在 C++ 中,new/delete
是推荐的方式,因为它们提供了更安全和便利的内存管理机制。
C++内存分配策略-CSDN博客