最近阅读了Thinking in C++一书, 其中对C++对象的构造,析构做了详细的解释。针对书上的内容,我编写了几个小的代码片段来加深这些问题的理解。
1. 构造,析构,new,delete函数的调用次序
</pre><p><pre name="code" class="cpp">#include <iostream>
#include <cstdlib>
using namespace std;
class A
{
public:
A() { cout << "construct of A" << endl; }
~A() { cout << "destruct of A" << endl; }
void* operator new(size_t sz) {
cout << "new of A" << endl;
void *m = malloc(sz);
return m;
}
void* operator new[](size_t sz) {
cout << "new[] of A" << endl;
void *m = malloc(sz);
return m;
}
void operator delete(void *m)
{
cout << "delete of A" << endl;
if (0==m)
return;
free(m);
}
void operator delete[](void *m)
{
cout << "delete of A[]" << endl;
if (0==m)
return;
free(m);
}
};
class B: public A
{
public:
B() { cout << "construct of B" << endl; }
~B() { cout << "destruct of B" << endl; }
void* operator new(size_t sz) {
cout << "new of B" << endl;
void *m = malloc(sz);
return m;
}
void* operator new[](size_t sz) {
cout << "new[] of B" << endl;
void *m = malloc(sz);
return m;
}
void operator delete(void *m)
{
cout << "delete of B" << endl;
if (0==m)
return;
free(m);
}
void operator delete[](void *m)
{
cout << "delete of B[]" << endl;
if (0==m)
return;
free(m);
}
};
运行结果:
new of B
construct of A
construct of B
destruct of B
destruct of A
delete of B
new()的操作是做了两件事情:一是为对象分配了内存,在上面的代码里是调用malloc来完成的。二是调用了构造函数来完成对象数据的初始化。以上代码的调用次序说明了在new的时候,先是分配了内存,然后调用基类的构造函数,最后才调用子类的构造函数。有了内存才能做后面构造函数的初始化,而基类对象的初始化也必须发生在子类对象初始化的前面,也就是说子类初始化的时候已经有了一个完备的基类对象数据。
delete的操作正好相反,先是要析构子类对象,释放资源,然后析构基类对象,最后将对象内存释放。
new和delete在基类和子类里都有重载,但只有子类的重载被调用。与普通函数和重载函数不同的是,编译器强制调用基类的构造函数和析构函数,来保证对象数据被正确的初始化和释放。
2. 数组对象的分配和释放
int main()
{
B *b;
b = new B[3];
delete []b;
//Core dump
//b = new B[3];
//delete b;
}
运行结果 new[] of B
construct of A
construct of B
construct of A
construct of B
construct of A
construct of B
destruct of B
destruct of A
destruct of B
destruct of A
destruct of B
destruct of A
delete of B[]
当用new来分配一个数组对象时,运算函数new[]()被调用而不是new()。每个对象的构造和析构过程还是和单个对象的情况一样。在释放数组对象时,要用delete[]而不是delete来销毁对象。如果使用delete,运行时会产生core dump。
3. Virtual Destruct
#include <iostream>
#include <cstdlib>
using namespace std;
class A
{
public:
A() { cout << "construct of A" << endl; }
virtual ~A() { cout << "destruct of A" << endl; }
};
class B: public A
{
public:
B() { cout << "construct of B" << endl; }
~B() { cout << "destruct of B" << endl; }
};
int main()
{
A *a = new B;
delete a;
}
运行结果
construct of A
construct of B
destruct of B
destruct of A
这里给基类A的析构函数加上了virtual修饰符。在使用多态性时,我们经常用基类(A)指针指向子类(B)的对象,如果基类的构造函数没有virtual,那么当通过基类指针销毁对象时,子类的析构函数不会被调用。这个其他普通函数类似,必须要定义virtual,才能通过基类虚函数表来找到子类的函数定义。
另一方面,构造函数是不能被定义为virtual的,也就是说编译器是严格按照基类和子类的顺序一个一个调用构造函数。如下的代码是会编译出错的。
class A
{
public:
virtual A() { cout << "construct of A" << endl; }
};