c++类成员初始化的三个地方:(1)声明处初始化、(2)构造函数初始化列表初始化、(3)构造函数体内初始化

在写这篇博客之前,笔者已经知道(2)构造函数初始化列表是在(3)构造函数体之前运行的,并且如果有多个成员,则按照成员声明的顺序依次初始化,而和初始化列表里面的先后顺序无关。笔者主要是不明白(1)声明处初始化和另外两处初始化的先后顺序。

一、先写一个小demo验证一下我说的第一句话。

#include<iostream>
#include<memory>
using namespace std;
class Type
{
public:
    explicit Type(int data):mData(data)
    { 
        cout<<"Type(int data):"<<this<<" mData="<<mData<<endl; 
    }
    Type(const Type& o)
    {
        mData = o.mData;
        cout<<"Type(const Type& o):"<<this<<" mData="<<mData<<endl;
    }
    Type& operator=(const Type& o)
    {
        cout<<"Type& operator=(const Type& o):"<<this<<" mData="<<o.mData<<endl;
        if(&o == this)
            return *this;
        mData = o.mData;
        return *this;
    }
    ~Type()
    {
        cout<<"~Type():"<<this<<" mData="<<mData<<endl;
    }
private:
    int mData;
};

class Container
{
public:
    Container():mType2(new Type(2)),mType3(new Type(3)),mType1(new Type(1))
    { 
        mType0 = make_shared<Type>(0);
        mType4 = make_shared<Type>(4);
        cout<<"Container()"<<endl;
    }
    ~Container(){cout<<" ~Container()"<<endl;}
private:
    shared_ptr<Type> mType0;
    shared_ptr<Type> mType1;
    shared_ptr<Type> mType2;
    shared_ptr<Type> mType3;
    shared_ptr<Type> mType4;
};

int main()
{
    Container c;
    return 0;
}

运行结果是:

Type(int data):0x561c60babe70 mData=1
Type(int data):0x561c60bac2c0 mData=2
Type(int data):0x561c60bac300 mData=3
Type(int data):0x561c60bac350 mData=0
Type(int data):0x561c60bac370 mData=4
Container()
 ~Container()
~Type():0x561c60bac370 mData=4
~Type():0x561c60bac300 mData=3
~Type():0x561c60bac2c0 mData=2
~Type():0x561c60babe70 mData=1
~Type():0x561c60bac350 mData=0

可以看见,尽管第35行是按照2-3-1的顺序写的,实际上初始化还是按照声明的1-2-3顺序来执行的。还可以看见,初始化列表里面的1-2-3初始化完成后,才会执行构造函数体里面的初始化代码。从打印出来的最后5行还可以看出来,析构的时候是按照声明的逆序来析构那5个智能指针的。

二、再看看 = 在什么情况下是调用拷贝赋值运算符,在什么情况下调用构造函数

#include<iostream>
using namespace std;
class Type
{
public:
    explicit Type(int data):mData(data)
    { 
        cout<<"Type(int data):"<<this<<" mData="<<mData<<endl; 
    }
    Type(const Type& o)
    {
        mData = o.mData;
        cout<<"Type(const Type& o):"<<this<<" mData="<<mData<<endl;
    }
    Type& operator=(const Type& o)
    {
        cout<<"Type& operator=(const Type& o):"<<this<<" mData="<<o.mData<<endl;
        if(&o == this)
            return *this;
        mData = o.mData;
        return *this;
    }
    ~Type()
    {
        cout<<"~Type():"<<this<<" mData="<<mData<<endl;
    }
private:
    int mData;
};

int main()
{
    Type type1(1);
    Type type2 = Type(2);
    type1 = Type(3);
    return 0;
}

运行结果是:

Type(int data):0x7ffcfe37c96c mData=1
Type(int data):0x7ffcfe37c970 mData=2
Type(int data):0x7ffcfe37c974 mData=3
Type& operator=(const Type& o):0x7ffcfe37c96c mData=3
~Type():0x7ffcfe37c974 mData=3
~Type():0x7ffcfe37c970 mData=2
~Type():0x7ffcfe37c96c mData=3

注意看,输出的第1行、第4行、第7行的内存地址是一样的。从运行结果可以看出来:第34行 Type type2 = Type(2);是调用Type类的构造函数给=号左边的type2初始化,这里并没有生成一个临时对象。第35行type1 = Type(3);则是先在=号右边生成一个临时对象,再把这个临时对象作为参数传进type1调用的Type& operator=(const Type& o),这是一句拷贝赋值的操作。第35行运行结束之后,临时对象析构打印出输出的第5行。

三、看看(1)相对于(2)和(3)的执行顺序

#include<iostream>
#include<memory>
using namespace std;
class Type
{
public:
    explicit Type(int data):mData(data)
    { 
        cout<<"Type(int data):"<<this<<" mData="<<mData<<endl; 
    }
    Type(const Type& o)
    {
        mData = o.mData;
        cout<<"Type(const Type& o):"<<this<<" mData="<<mData<<endl;
    }
    Type& operator=(const Type& o)
    {
        cout<<"Type& operator=(const Type& o):"<<this<<" mData="<<o.mData<<endl;
        if(&o == this)
            return *this;
        mData = o.mData;
        return *this;
    }
    ~Type()
    {
        cout<<"~Type():"<<this<<" mData="<<mData<<endl;
    }
private:
    int mData;
};

class Container
{
public:
    Container():mType7(new Type(7)),mType4(new Type(4)),mType5(new Type(5)),mType3(new Type(3))
    { 
        mType2= make_shared<Type>(2);
        cout<<"Container()"<<endl;
    }
    ~Container(){cout<<" ~Container()"<<endl;}
private:
    shared_ptr<Type> mType1 = make_shared<Type>(1);
    shared_ptr<Type> mType2;
    shared_ptr<Type> mType3;
    shared_ptr<Type> mType4;
    shared_ptr<Type> mType5;
    shared_ptr<Type> mType6= make_shared<Type>(6);
    shared_ptr<Type> mType7;
};

int main()
{
    Container c;
    return 0;
}

运行结果是:

Type(int data):0x562184c94e80 mData=1
Type(int data):0x562184c952a0 mData=3
Type(int data):0x562184c952e0 mData=4
Type(int data):0x562184c95320 mData=5
Type(int data):0x562184c95370 mData=6
Type(int data):0x562184c95380 mData=7
Type(int data):0x562184c953d0 mData=2
Container()
 ~Container()
~Type():0x562184c95380 mData=7
~Type():0x562184c95370 mData=6
~Type():0x562184c95320 mData=5
~Type():0x562184c952e0 mData=4
~Type():0x562184c952a0 mData=3
~Type():0x562184c953d0 mData=2
~Type():0x562184c94e80 mData=1

从结果可以看出来,构造函数体里面的初始化代码是在最后执行的。(1)[声明处初始化]和(2)[构造函数初始化列表初始化]之间并没有绝对的先后顺序。而是根据成员的声明顺序来执行的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值