Masimaro's blog - 构造与析构
type: channel
link: https://www.masimaro.top/tag/%E6%9E%84%E9%80%A0%E4%B8%8E%E6%9E%90%E6%9E%84/
items: C++类的构造函数与析构函数

C++类的构造函数与析构函数
type: item
date: 2016-06-29T09:42:00+08:00
description: C++中每个类都有其构造与析构函数，它们负责对象的创建和对象的清理和回收，即使我们不写这两个，编译器也会默认为我们提供这些构造函数。下面仍然是通过反汇编的方式来说明C++中构造和析构函数是如何工作的。编译器是否真的会默认提供构造与析构函数在一般讲解C++的书籍中都会提及到当我们不为类提供任何构造与析构函数时编译器会默认提供这样六种成员函数：不带参构造，拷贝构造，“=”的重载函数，析构函数，以及带const和不带const的取地址符重载。但是编译器具体是怎么做的，下面来对其中的部分进行说明不带参构造下面是一个例子：class test { private: char szBuf[255]; public: static void print() { cout<<"hello world"; } }; void printhello(test t) { t.print(); } int main(int argc, char* argv[]) { test t; printhello(t); return 0; }下面是对应的汇编源码：00401400 push ebp 00401401 mov ebp,esp 00401403 sub esp,140h;栈顶向上抬了140h的空间用于存储类对象的数据 00401409 push ebx 0040140A push esi 0040140B push edi 0040140C lea edi,[ebp-140h] 00401412 mov ecx,50h 00401417 mov eax,0CCCCCCCCh 0040141C rep stos dword ptr [edi] 26: test t; 27: printhello(t); 0040141E sub esp,100h从上面可以看到，在定义类的对象时并没有进行任何的函数调用，在进行对象的内存空间分配时仅仅是将栈容量扩大，就好像定义一个普通变量一样，也就是说在默认情况下编译器并不会提供不带参的构造函数，在初始化对象时仅仅将其作为一个普通变量，在编译之前计算出它所占内存的大小，然后分配，并不调用函数。再看下面一个例子：class test { private: char szBuf[255]; public: virtual void sayhello() { cout<<"hello world"; } static void print() { cout<<"hello world"; } }; void printhello(test t) { t.print(); } int main(int argc, char* argv[]) { test t; printhello(t); return 0; }30: test t; 0040143E lea ecx,[ebp-104h] 00401444 call @ILT+140(test::test) (00401091) ;构造函数 004014A0 push ebp 004014A1 mov ebp,esp 004014A3 sub esp,44h 004014A6 push ebx 004014A7 push esi 004014A8 push edi 004014A9 push ecx 004014AA lea edi,[ebp-44h] 004014AD mov ecx,11h 004014B2 mov eax,0CCCCCCCCh 004014B7 rep stos dword ptr [edi] 004014B9 pop ecx 004014BA mov dword ptr [ebp-4],ecx 004014BD mov eax,dword ptr [ebp-4] 004014C0 mov dword ptr [eax],offset test::`vftable' (0042f02c) 004014C6 mov eax,dword ptr [ebp-4] 004014C9 pop edi 004014CA pop esi 004014CB pop ebx 004014CC mov esp,ebp 004014CE pop ebp这段C++代码与之前的仅仅是多了一个虚函数，这个时候编译器为这个类定义了一个默认的构造函数，从汇编代码中可以看到，这个构造函数主要初始化了类对象的头4个字节，将虚函数表的地址放入到这个4个字节中，因此我们得出结论，一般编译器不会提供不带参的构造函数，除非类中有虚函数。下面请看这样一个例子：class Parent { public: Parent() { cout<<"parent!"<<endl; } }; class ch…