C++ 构造和析构

啥是构造和析构,下面我们还是从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 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值