C++中创建堆对象时,我们要使用new/delete,那么它们具体做了什么?现在深入讨论。
1. new表达式工作步骤
使用new表达式时发生的三个步骤:
- 调用名为operator new的标准库函数来分配足够大的原始的未类型化的堆空间,以保存指定类型的一个对象;
- 运行该类型的一个构造函数初始化对象;
- 返回指向新分配并构造的构造函数对象的指针。
2. delete表达式工作步骤
使用delete表达式时发生的两个步骤:
- 调用析构函数,回收对象中数据成员所申请的资源;
- 调用名为operator delete的标准库函数来释放该对象本身所占用的内存。
3. operator new和operator delete函数的重载
//operator new库函数
void *operator new(size_t);
void *operator new[](size_t);
//operator delete库函数
void operator delete(void *);
void operator delete[](void *);
通过例子说明:
#include <string.h>
#include <stdlib.h>
#include <iostream>
using std::cout;
using std::endl;
void *operator new(size_t sz)
{
cout << "void *operator new(size_t)" << endl;
void *pret = malloc(sz);
return pret;
}
void operator delete(void *ptr)
{
cout << "void operator delete(void *)" << endl;
free(ptr);
}
class Student
{
public:
Student(int id, const char* name)
: _id(id)
, _name(new char[strlen(name)+1]())
{
cout << "Student(int, char*)" << endl;
strcpy(_name, name);
}
#if 0
void *operator new(size_t sz)//等价:static void *operator new(size_t sz)
{
cout << "void *operator new(size_t)" << endl;
/*Q:成员函数operator new/delete里面包含this没有?
/*A: printf("this: %p\n", this); error,类中默认是静态成员函数*/
//申请的原始的未初始化的空间
void *pret = malloc(sz);
return pret;
}
static void operator delete(void *ptr)//等价:void operator delete(void *ptr)
{
cout << "void operator delete(void *)" << endl;
free(ptr);
}
#endif
void print() const
{
if(_name)
{
cout << "(id = " << _id << ", "
<< "name = " << _name
<< ")" << endl;
}
}
~Student()
{
cout << "~Student()" << endl;
if(_name)
{
delete [] _name;
_name = nullptr;
}
}
private:
int _id;
char* _name;
};
void test()
{
Student *pstu = new Student(4306, "xiaozhang");//堆对象
pstu->print();
/*
A:对象的销毁与析构函数的执行是不是等价呢?
Q:如果是栈对象,那么对象的销毁与析构函数的执行就是等价的;
对于堆对象而言,对象的销毁会执行析构函数,但析构函数的执行
只是对象销毁中的一个步骤。
*/
delete pstu;
pstu = nullptr;
}
int main(int argc, char* argv[])
{
test();
return 0;
}
运行结果分析:
经过上述的讨论,结合new和delete工作机制,现在有两个问题:
Q1:如果要求一个类只能创建栈对象,怎么处理?(其意思是在创建出栈对象的同时,不能创建堆对象)
A1:不能简单的将构造函数设置为私有,否则栈对象、堆对象都无法创建,解决方案是将operator new库函数设置为私有的。
Q2:如果要求一个类只能创建堆对象,怎么处理?(其意思是在创建出堆对象的同时,不能创建栈对象)
A2:由于创建栈对象的条件需要将构造函数和析构函数设置为public,可以将析构函数设置为私有。而销毁堆对象时会有报错,可以有类似如下操作:
class Student
{
//...
void destroy()
{
/*this->~Student();//显示调用析构函数*/
delete this;
}
private:
~Student()
{
cout << "~Student()" << endl;
/* delete this;error,栈溢出*/
if(_name)
{
delete [] _name;
_name = nullptr;
}
}
}
//...
pstu->destroy();