啥是构造和析构,下面我们还是从C语言的角度来学习C++,简述构造和析构语法的设计由来。
先看如下代码:
struct Test
{
int a;
int b;
};
int main()
{
Test test;
test.a = 5;
test.b = 7;
printf("%d %d",test.a,test.b);
return 0;
}
代码应该很简单,其本意其实是在创建对象的时候初始化a和b,所以我们最好再改进下,更符合我们的编程逻辑
struct Test
{
int a;
int b;
void init(int a=0,int b=0)
{
this->a = a;
this->b = b;
}
};
int main()
{
Test test;
test.init(5,7); //调用表示初始化
printf("%d %d",test.a,test.b);
return 0;
}
上面的代码配上了缺省参数(对这个不理解的可以上一篇博客),看着代码逻辑清晰多了。
下面我们考虑一个问题,作为我们(程序员)来说,会不会有可能忘记调用或者调用多次的情况呢?
这个情况是有的,毕竟封装这个类的人和调用者可能不是同一个人哦。但是为啥调用多次会有问题呢?我们可以考虑下类里面有指针指向堆内存的情况
struct Test
{
int a;
int b;
char *str;
void init(int a=0,int b=0)
{
this->a = a;
this->b = b;
str = (char*)malloc(20);
}
};
当多次调用后就会存在内存泄露的问题,因为之前的str指向的堆内存可能没释放
所以 引出了构造,下面看一下构造的语法,使用构造替换init成员函数
struct Test
{
int a;
int b;
char *str;
// 1. 与类同名
// 2. 没有返回值
Test(int a=3,int b=7)
{
this->a = a;
this->b = b;
str = (char*)malloc(20);
}
};
int main()
{
Test test; // 3.创建对象的时候执行
printf("%d %d",test.a,test.b);
return 0;
}
首先看第一点,同名这个是语法规定,第二点,无返回值,你使用返回值时,会报如下错误
void Test(int a=3,int b=7)
type(s) preceding 'Test' (constructor with return type, or illegal redefinition of current class-name?)
第三点的话我们来看一下汇编
22: Test test; // 3.创建对象的时候执行
00401268 6A 07 push 7 ;缺省参数
0040126A 6A 03 push 3 ;缺省参数
0040126C 8D 4D F4 lea ecx,[ebp-0Ch]
0040126F E8 91 FD FF FF call @ILT+0(Test::Test) (00401005) ;调用构造
可以看出来,调用初始化这个活只是编译器帮我们干了,所以上面说的忘记调用应该是不会有了,那么重复调用呢,我们来试一下
int main()
{
Test test; // 3.创建对象的时候执行
//test.Test(1,2); err
test.Test::Test(1,2); //ok 编译器bug
printf("%d %d",test.a,test.b);
return 0;
}
平常情况来说,使用对象显示调用构造是会报错的,下面那个算是编译器bug吧,这里就不深究了
下面,我们把Test构造中的缺省参数去掉,编译看看如何?
'Test' : no appropriate default constructor available
编译报错,没有合适的默认构造可用,那么我们再添加一个空参数构造
Test()
{
this->a = 5;
this->b = 7;
this->str = NULL;
}
Test(int a,int b)
{
this->a = a;
this->b = b;
this->str = NULL;
}
OK,编译通过,说明构造是可以重载的(对于重载可以看上一篇内容)
26: Test test;
00401268 8D 4D F4 lea ecx,[ebp-0Ch]
0040126B E8 A4 FD FF FF call @ILT+15(Test::Test) (00401014)
观察汇编,发现其调用了无参的Test构造,那么这里你可能会有个疑问,既然是构造函数,我可以这样写么
Test test(); //err
为什么会编译不过呢?这里的话,其实细细看一下,这行代码是不是比较像函数声明呢,所以编译器报错了。
下面再来思考一个问题,构造函数是必须的么,无构造函数的时候编译器会帮我们默认生成么
其实前面这个问题拿一开始的例子就可以解决了,因为我们一开始调用了inti方法进行初始化,并没有写构造方法,使用构造是可以缺省的,那编译器会 帮我们生成么,下面我们看一下汇编
13: Test test;
14: printf("%d %d",test.a,test.b);
00401268 8B 45 FC mov eax,dword ptr [ebp-4]
0040126B 50 push eax
0040126C 8B 4D F8 mov ecx,dword ptr [ebp-8]
0040126F 51 push ecx
00401270 68 1C 10 43 00 push offset string "%d %d" (0043101c)
00401275 E8 36 70 00 00 call