new and malloc
new 和 delete
先看看下面的例子:
#include <iostream>
#include <malloc.h>
using namespace std;
class T
{
public:
T(){v = 666;}
~T(){cout << "delete T\n";}
void show(){cout << v << endl;}
private:
int v;
};
int main()
{
cout << "new:\n";
T* t1 = new T;
t1->show();
delete t1;
cout << "malloc:\n";
T* t2= (T*)malloc(sizeof(T));
if(t2 != nullptr)
{
t2->show();
free(t2);
}
return 0;
}
输出如下:
new:
666
delete T
malloc:
16325248
- new 会调用对象构造函数,在堆区新建一个对象,并返回该对象的指针(失败抛出 bad_alloc 异常)。
- malloc 在堆区分配一块内存,用mallco在堆区创建一个对象是不会调用构造函数的(失败返回 nullptr)。
- delete 会释放堆区内存,并调用对应的析构函数。
- free 只会释放内存。
new 创建对象
- 调用用operatoe new()为对象分配内存(常用malloc)
- 调用构造函数来初始化内存。
delete 销毁对象
- 调用析构函数。
- 调用operator delete() 释放内存(常用free)。
构造和析构函数的调用是由编译器控制的,可以重载 operator new()和operator delete()。
why new delete
- 在OOP中,需要在创建对象同时要自动执行构造函数,在对象销毁之前需要自动执行析构函数。
- malloc、free是C、C++标准库函数,编译器无法控制其行为,不能让其在执行时强制执行构造和析构函数。
- new、delete 是C++运算符,由编译器控制其行为,可以让其在执行时自动执行构造和析构函数。
operator new()
- param1:size_t
- ......(placement new)
- return:void*
operator new
重载可以放在全局或类内部。当编译器发现有
new
关键字,会在当前类和基类中寻找operator new
,找不到就在全局中找,再找不到就用默认的。类中的
operator new
默认static
。
class T
{
public:
T(){v = 666;}
~T(){cout << "delete T\n";}
void show(){cout << v << endl;}
void* operator new(size_t size)
{
cout << "operator new(std::size_t size)\n";
return malloc(size);
}
private:
int v;
};
int main()
{
cout << "new:\n";
T* t1 = new T;
t1->show();
delete t1;
return 0;
}
输出:
new:
operator new(std::size_t size)
666
delete T
placement new
是 operator new
一种重载形式
class T
{
public:
T(int _v):v(_v){}
~T(){cout << "delete T\n";}
void show(){cout << v << endl;}
void* operator new(size_t size)
{
cout << "operator new(std::size_t size)\n";
return malloc(size);
}
void* operator new(size_t size, void* ptr)
{
cout << "operator new(size_t size, void* ptr)\n";
return ptr;
}
private:
int v;
};
int main()
{
cout << "new:\n";
T* t1 = new T(1);
t1->show();
T* t2 = new(t1) T(2);
t1->show();
t2->show();
printf("%x\n", t1);
printf("%x\n", t2);
//delete t2;
delete t1;
return 0;
}
输出:
new:
operator new(std::size_t size)
1
operator new(size_t size, void* ptr)
2
2
7e2a90
7e2a90
delete T
可见这里的t1 和 t2都指向同一个地址,因此delete一个就行。
placement new
不需要申请新内存和释放旧内存,可以用在内存池里面。operator new
可以有其它参数,这里不过多赘述。operator delete
重载和operator new
相同,但一般不会重载,因为重载版本delete
不可手动调用。
new[] 和 delete[]
这两个是针对数组的版本,看下面的例子:
class A
{
public:
A() { cout << "create A\n"; };
~A() { cout << "delete A\n"; };
unsigned char ch{ 'a' };
};
int main()
{
constexpr const size_t N = 3;
A* p = new A[N];
cout << *((size_t*)p - 1) << endl;
delete[] p;
return 0;
}
输出:
create A
create A
create A
3
delete A
delete A
delete A
可见 new[] 会对每一个对象调用一次构造函数,delete[] 会对每一个对象调用一次析构函数。
C++ 标准里面的相关定义如下:
void *operator new(size_t);
void *operator delete(void *);
void *operator new[](size_t);
void *operator delete[](void *);
并且部分实现如下:(仍然调用了operator new())
void* operator new[] (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)
{
/*......*/
return ::operator new(sz);
}
内建数据类型:new[] 基本与 new 等价,delete[] 与 delete 也类似。
构造函数不带参数:编译器会在返回指针前面(低地址处)多分配 sizeof(size_t) 的空间用于储存对象个数。
这里要求该类要显示的实现析构函数才能拿到对应的数组长度(MSVC)。
// ~A() { cout << "delete A\n"; }; // N == *((size_t*)p - 1)
编译器默认函数
C++ 编译器默认在类里面会生成下面的函数:
A(); //默认构造
~A(); //默认析构
A(const A&); //默认(浅)拷贝
A& operator = (const A& a); //默认赋值
C++ 中不允许定义值传递的拷贝构造函数(如下):
A(A a){} // ERROR
原因
值传递的拷贝构造函数在传参过程中要调用拷贝构造函数本身,在调用拷贝构造函数时要先进行参数传递,参数传递又要调用拷贝构造函数,会不停递归分配堆栈。
赋值函数是把一个对象赋值给另一个已存在的对象,使得该对象和原对象属性一致。
不想编译器默认生成上面的函数,可以作为 private 或在后面加上 =delete。
If it is useful, and you would like to help me, please Money🤡Money🤡Money🤡!
- 本文链接:https://morisa66.github.io/2021/03/01/NewMalloc/
- 版权声明:本博客所有文章除特别声明外,均默认采用 许可协议。