new 一个 "女朋友" 需要几步:C++ new, new[]解析

在单身的程序员中间,经常调侃女朋友 可以 new 一个,那么问题来了你知道new一女朋友需要几步吗?

在C++中new, new[] 作为关键字,也作为运算符可以被重载,下面我们来理解一下编译器在 new 和 new[]时都做了什么

先来定义 GirlFriend

class GirlFriend {
public:
string name;
int age;
    
    GirlFriend() {          
        this->name = "unknow";
        this->age = 0;
    }
    GirlFriend(const string& name, int age)
        : name(name), age(age)
    {}
}

当你 用 new 创建 GirlFriend 时你可以这样做:

GirlFriend* myGirlFriend = new GirlFriend();

编译器看到这样的代码会将其解释为类似这样的代码:

// 如果重载了operator new 这样调用
try{
void* mem = GirlFriend::operator new(sizeof(GirlFriend));
GirlFriend* myGirlFriend = static_cast<GirlFriend*>(mem);
myGirlFriend->GirlFriend::GirlFriend(); //调用构造函数
}catch(...)
{
// 不执行构造函数
}
// 如果没有重载 operator new 这样调用
 
void* mem = operator new(sizeof(GirlFriend));
GirlFriend* myGirlFriend = static_cast<GirlFriend*>(mem);
myGirlFriend->GirlFriend::GirlFriend(); //调用构造函数
 

最终保证了 myGirlFriend 的类型为GirlFriend* 且调用构造函数

其中 operator new 是一个函数(内建)

如果你想在GirlFriend类中重载 operator new 可以这样做:

 

class GirlFriend {
public:
    string name;
    int age;

    GirlFriend() = default;
    GirlFriend(const string& name, int age)
        : name(name), age(age)
    {}
    
    static void* operator new(size_t size) {    // static 可写可不写
        cout << "You are creating your girl friend" << endl;
        return malloc(size);
    }
}

这里有几点注意 operator new 的返回类型只能是 void*, 且第一个参数必须是 size_t 类型, 其背后的原因就在于,operator new 最终调用的是 malloc 函数,malloc 函数接收size_t 类型的参数,返回void*

 因而,operator new 只能返回 void*, 编译器会自动的将其最终转化为对应的类型

 除此之外 operator new 还支持带(其他)参数的重载比如这样

class GirlFriend {
public:
string name;
int age;
	GirlFriend() {          
		this->name = "unknow";
		this->age = 0;
	}
    GirlFriend(const string& name, int age)
        : name(name), age(age)
    {}
    
    static void* operator new(size_t size) {      // static 可写可不写
        cout << "You are creating your girl friend" << endl;
        return malloc(size);
    }

	static void* operator new(size_t size, GirlFriend* ptr) {  // static 可写可不写
		cout << "another girl friend based on previous malloced memory" << endl;
		return ptr;
	}
};

其中 static void* operator new(size_t size, GirlFriend* ptr) 这样的 重载在调用时 这样传参

GirlFriend* gf1 = new GirlFriend("Brown", 19);
GirlFriend* gf2 = new(lf) GirlFriend();

这两个 new 编译器在处理时,将其处理为如下 代码逻辑


void* mem = GirlFriend::operator new(sizeof(GirlFriend));
GirlFriend* gf1 = static_cast<GirlFriend*>(mem);
gf1->GirlFriend::GirlFriend("Brown", 19);

void* mem = GirlFriend::operator new(sizeof(GirlFriend), gf1);
GirlFriend* gf2 = static_cast<GirlFriend*>(mem);
gf2->GirlFriend::GirlFriend();

让我通过一段代码来理解和验证一下上述内容


#include <iostream>
using namespace std;

class GirlFriend {
public:
string name;
int age;
	GirlFriend() {
		this->name = "unknow";
		this->age = 0;
	}
    GirlFriend(const string& name, int age)
        : name(name), age(age)
    {}
    
    static void* operator new(size_t size) {
        cout << "You are creating your girl friend." << endl;
        return malloc(size);
    }
    static void* operator new(size_t size, GirlFriend* ptr) {
        cout << "You are creating your girl friend with used memory." << endl;
        return ptr;
    }
};


int main(void)
{
    GirlFriend* gf1 = new GirlFriend("Brown", 19);
    GirlFriend* gf2 = new(gf1) GirlFriend();

    cout << gf2->age << endl;
    cout << gf2->name << endl;

    system("pause");
    return 0;

}

运行结果如下:

You are creating your girl friend.
You are creating your girl friend with used memory.
0
unknow
Press any key to continue . . .

讲完了new 下面来讲一下 new[], 基于以上的代码, 

如果 我们 想要 同时new 多个 GirlFriend时, 自让会想到 诸如这样的写法

int N = 3;

GirlFriend* gfs = new GirlFriend[N];

那么编译器在看到这行代码时,是怎么处理的呢,其实类似new 的处理方法,它会将其转化为如下的代码逻辑,注意我指的是代码逻辑,不是指编译器就会直接这么做

void* mem = (void*)((int(GirlFriend::operator new[](sizeof(GirlFriend) * (N)+4)) + 4));
GirlFriend* gfs = static_cast<GirlFriend*>(mem);
for (int i = 0; i < N; ++i)
	(gfs + i)->GirlFriend::GirlFriend();

至于为什么+4,这多余的四字节空间存放数组的长度,也就是N的值, 这四个字节的地址空间在返回地址的下面,这也就是为什么最后又+1(int*的步长为4),下面我们通过 如下代码来验证一下

#include <iostream>
using namespace std;

class GirlFriend {
public:
	string name;
	int age;
	GirlFriend() {
		this->name = "unknow";
		this->age = 0;
	}
	GirlFriend(const string& name, int age)
		: name(name), age(age)
	{}

	static void* operator new(size_t size) {
		cout << "You are creating your girl friend." << endl;
		return malloc(size);
	}
	static void* operator new(size_t size, GirlFriend* ptr) {
		cout << "You are creating your girl friend with used memory." << endl;
		return ptr;
	}
};


int main(void)
{
	GirlFriend* gfs = new GirlFriend[10];
	cout << *((int*)gfs -1) << endl;


	system("pause");
	return 0;
}

运行结果:

10
Press any key to continue . . .

这里补充一下,只有我们创建的自定义类型(用 class, struct 创建)在调用new[]时会多开辟4个字节存放new的个数,目的是为了正确的执行对应个数个的构造函数和析构函数,像C++的内建类型(int, float, double, char ...) 则不会多开辟4个字节,因为他们没有构造函数和析构函数,不需要保存数组长度。

operator new[] 也是一个内建的函数, 当然你也可以在类内重载它。

由于operator new[]的这样编译机制,因而,其实它重载的方式 和 new 相差不大,都要求返回值必须为void*, 第一个参数必须是size_t 类型,下面我直接重载operator new[], 并演示其运行结果。

#include <iostream>
using namespace std;

class GirlFriend {
public:
string name;
int age;
	GirlFriend() {
		this->name = "unknow";
		this->age = 0;
	}
    GirlFriend(const string& name, int age)
        : name(name), age(age)
    {}
    
    static void* operator new(size_t size) {       // static 可写可不写
        cout << "You are creating your girl friend." << endl;
        return malloc(size);
    }

	static void* operator new(size_t size, GirlFriend* ptr) {    // static 可写可不写
		cout << "You are creating your girl friend with used memory." << endl;
		return ptr;
	}

	static void* operator new[](size_t size) {     // static 可写可不写
        cout << "You are creating your girl friends ......" << endl;
        return malloc(size);
	}

	static void* operator new[](size_t size, list<void*>& lt) {    // static 可写可不写
			
        cout << "You are ..... ......" << endl;
		void* tmp = malloc(size);
		lt.push_back(tmp);
		return tmp;
	}
};


int main(void)
{

	GirlFriend* gf1 = new GirlFriend();
	GirlFriend* gf2 = new(gf1) GirlFriend;
	delete gf2;

	GirlFriend* gfs = new GirlFriend[10]{{"Brown", 19}};
	for (int i = 0; i < 10; ++i)
		cout << (gfs+i)->name << endl;
	delete[] gfs;

	list<void*> girlList;

	GirlFriend* gfss1 = new (girlList) GirlFriend[10];
	for (int i = 0; i < 10; ++i)
		cout << (gfss1+i)->name << endl;

	GirlFriend* gfss2 = new (girlList) GirlFriend[20];

	for (auto& ptr : girlList)
		delete[] ptr;

	system("pause");
	return 0;

}

结果:

You are creating your girl friend.
You are creating your girl friend with used memory.
You are creating your girl friends ......
Brown
unknow
unknow
unknow
unknow
unknow
unknow
unknow
unknow
unknow
You are ..... ......
unknow
unknow
unknow
unknow
unknow
unknow
unknow
unknow
unknow
unknow
You are ..... ......

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值