当前位置:网站首页>重载全局和成员new/delete
重载全局和成员new/delete
2022-07-02 06:18:00 【从此开始低调范️】
1. 重写全局new/delete
重载::operator new、::operator delete、::operator new[]、operator delete[]
注意:如果重载的是全局的,所以创建对象时编译器调用operator new、operator delete时都会去调用自己重载的版本;如果重载的类成员operator new,那么只在创建和释放该类对象时编译器调用自己重载的operator new、operator delete版本。
如果Fraction类没有重写本来的new/delete,那么会直接调用全局的new/delete:
class Fraction
{
public:
Fraction(int num, int den = 1) :m_numerator(num), m_denominator(den) {}
private:
int m_numerator;//分子
int m_denominator;//分母
};
void* myAlloc(size_t size)
{
return malloc(size);
}
inline void* operator new(size_t size)
{
cout << "调用了重载的全局new\n" << endl;
return myAlloc(size);
}
void operator delete(void* ptr /*,size_t*/)//第二个参数有默认值,不写也没有影响
{
cout << "调用了重载的全局delete\n" << endl;
free(ptr);
}
int main()
{
Fraction* f = new Fraction(1);//调用了重载的全局new
delete f;//调用了重载的全局delete
}
2. 重写成员new/delete
重写new和new[]区别就是new只会调用一次构造函数,而new[]会调用多次(次数就是[]内数字)。
class Foo
{
public:
int m_id;
long m_data;
string m_str;
public:
Foo() :m_id(0) { cout << "无参构造函数调用" << endl; }
Foo(int i) :m_id(i) { cout << "有参构造函数调用" << endl; }
~Foo() { cout << "析构函数调用" << endl; }
static void* operator new(size_t size)
{
cout << "operator new 调用,size = " << size << endl;
Foo* p = (Foo*)malloc(size);
return p;
}
static void operator delete(void* ptr, size_t size)
{
cout << "operator delete 调用,size = " << size << endl;
free(ptr);
}
static void* operator new[](size_t size)
{
cout << "operator new[] 调用,size = " << size << endl;
Foo* p = (Foo*)malloc(size);
return p;
}
static void operator delete[](void* ptr, size_t size)
{
cout << "operator delete[] 调用,size = " << size << endl;
free(ptr);
}
};
int main()
{
cout << sizeof(Foo) << endl;//48
Foo* pf = new Foo;//调用自己重写的operator new
delete pf;//调用自己重写的operator delete
Foo* gnew_pf = ::new Foo;//会绕过自己重写的operator new,强制调用全局的new
::delete gnew_pf;//会绕过自己重写的operator delete,强制调用全局的delete
Foo* pfAry = new Foo[3];
delete[] pfAry;
}
编译输出:
48
operator new 调用,size = 48
无参构造函数调用
析构函数调用
operator delete 调用,size = 48
无参构造函数调用
析构函数调用
operator new[] 调用,size = 152
无参构造函数调用
无参构造函数调用
无参构造函数调用
析构函数调用
析构函数调用
析构函数调用
operator delete[] 调用,size = 152
由结果可以看出,Foo类大小为48字节,调用operator new后,开辟size为48字节;调用operator new[3]后,开辟size为152字节,operator new[]和operator delete[]均只调用一次,构造和析构调用了3次。
调用operator new[3]本该开辟size为48*3=144字节空间,但是为什么开辟了152字节呢?
这是因为开辟数组空间时,在64位平台下需要有一个8字节大小的空间用来存放开辟的数组成员个数。32位平台下这个大小为4字节。
3. placement new
我们可以重载类成员operator new(),写出多个版本,前提就是每一版本的声明都必须有独特的参数列,其中第一参数必须是size_t,其余参数以new所指定的placement arguments为初值。出现在new(…)小括号内的便是所谓placement arguments。
Foo *pf = new(300,'c') Foo;// new()的实际参数是三个
例如:
//1.这个就是一般的operator new()的重载
static void* operator new(size_t size)
{
cout << "operator new 调用,size = " << size << endl;
Foo* p = (Foo*)malloc(size);
return p;
}
//2.这个就是标准库已提供的placement new()的重载形式
void* operator new(size_t size, void* start)
{
return start;
}
//3.这个是崭新的placement new
void* operator new(size_t size, long extra)
{
cout << "extra被调用" << endl;
return malloc(size + extra);
}
//4.这又是一个placement new
void* operator new(size_t size, long extra, char init)
{
return malloc(size + extra);
}
Foo* extrapf = new(200) Foo;//调用的就是第3个placement new。
我们也可以重载类成员operator delete(),写出多个版本。但它们绝不会被delete调用。只有当new所调用的构造函数抛出异常exception,才会调用对应operator new()重载版本的operator delete()。它只可能这样被调用,主要用来归还未能完全创建成功的对象所占用的内存。
#include <iostream>
using namespace std;
//自己的异常类
class Bad : public exception
{
public:
Bad(const char* err) {
this->m_pErr = new char[strlen(err) + 1];
strcpy_s(this->m_pErr, strlen(err) + 1, err);
}
~Bad() {
if (this->m_pErr != NULL) {
delete[] this->m_pErr;
this->m_pErr = NULL;
}
}
virtual const char* what() const
{
return m_pErr;
}
public:
char* m_pErr;
};
class Foo
{
public:
int m_id;
long m_data;
string m_str;
public:
Foo() :m_id(0) { cout << "无参构造函数调用" << endl; }
Foo(int i) :m_id(i)
{
cout << "有参构造函数调用" << endl;
//此处故意抛出异常,后面对应的placement delete才被调用
throw Bad("构造函数抛出异常了");//如果不抛出异常,只会调用一般的operator delete。
}
~Foo() { cout << "析构函数调用" << endl; }
//1.这个就是一般的operator new()的重载
static void* operator new(size_t size)
{
cout << "operator new 调用,size = " << size << endl;
Foo* p = (Foo*)malloc(size);
return p;
}
//2.这个就是标准库已提供的placement new()的重载形式
void* operator new(size_t size, void* start)
{
return start;
}
//3.这个是崭新的placement new
void* operator new(size_t size, long extra)
{
cout << "extra new被调用" << endl;
return malloc(size + extra);
}
//4.这又是一个placement new
void* operator new(size_t size, long extra, char init)
{
return malloc(size + extra);
}
//1.这是一个一般的operator delete()重载
static void operator delete(void* ptr, size_t size)
{
cout << "operator delete 调用,size = " << size << endl;
free(ptr);
}
//2.这是对应2new的delete
void operator delete(void*, void*)
{
cout << "delete(void*, void*)" << endl;
}
//3.这是对应3new的delete
void operator delete(void* ptr, long size)
{
//此处的size=200
cout << "extra delete被调用,size = " << size << endl;
free(ptr);
}
//4.这是对应4new的delete
void operator delete(void*, long, char)
{
cout << "operator delete(void*, long, char)" << endl;
}
};
int main()
{
//对应的placement delete被调用了
try
{
Foo* extrapf = new(200) Foo(1);
delete extrapf;
}
catch (Bad& e) {
cout << e.what() << endl;
}
}
编译输出:
extra new被调用
有参构造函数调用
extra delete被调用,size = 200
构造函数抛出异常了
使用placement new去开辟内存空间时,在构造函数发出异常情况下,即使释放时没有写出对应的placement delete函数,编译器也不会有任何报错。默认意思就是:我们放弃了处理构造函数抛出的异常。异常此时会默认向上传递(operator delete也不会被调用)。
边栏推荐
- Reading classic literature -- Suma++
- Compte à rebours de 3 jours pour l'inscription à l'accélérateur de démarrage Google Sea, Guide de démarrage collecté à l'avance!
- 官方零基础入门 Jetpack Compose 的中文课程来啦!
- LeetCode 90. Subset II
- MySQL的10大經典錯誤
- CUDA中的Warp Shuffle
- VLAN experiment of switching technology
- Classic literature reading -- deformable Detr
- IPv6 experiment and summary
- Don't use the new WP collection. Don't use WordPress collection without update
猜你喜欢
Ros2 --- lifecycle node summary
最新CUDA环境配置(Win10 + CUDA 11.6 + VS2019)
数据科学【九】:SVD(二)
Eco express micro engine system has supported one click deployment to cloud hosting
Decryption skills of encrypted compressed files
Little bear sect manual query and ADC in-depth study
LeetCode 90. Subset II
【张三学C语言之】—深入理解数据存储
CUDA中的线程层次
穀歌出海創業加速器報名倒計時 3 天,創業人闖關指南提前收藏!
随机推荐
最新CUDA环境配置(Win10 + CUDA 11.6 + VS2019)
LeetCode 27. Removing Elements
Compte à rebours de 3 jours pour l'inscription à l'accélérateur de démarrage Google Sea, Guide de démarrage collecté à l'avance!
Bgp Routing preference Rules and notice Principles
TensorRT的数据格式定义详解
栈(线性结构)
Support new and old imperial CMS collection and warehousing tutorials
Contest3147 - game 38 of 2021 Freshmen's personal training match_ 1: Maximum palindromes
Frequently asked questions about jetpack compose and material you
深入了解JUC并发(二)并发理论
The Chinese word segmentation task is realized by using traditional methods (n-gram, HMM, etc.), neural network methods (CNN, LSTM, etc.) and pre training methods (Bert, etc.)
BGP报文详细解释
BGP中的状态机
LeetCode 77. combination
LeetCode 90. Subset II
When requesting resttemplate, set the request header, request parameters, and request body.
链表(线性结构)
Linked list (linear structure)
深入学习JVM底层(三):垃圾回收器与内存分配策略
VRRP之监视上行链路