C++类对象实例的初始化方法,深入理解C++类对象的初始化语句,A a=A();内部机制到底是什么?初始化语句到底有没有临时无名对象?


从class A{}; 开始

默认构造函数与默认复制构造函数

这里已经默认生成了默认构造函数(无参的),即:

    A() //无参构造函数
    {
    	啥都不做
    }

和默认复制构造函数(参数是const A &),即:

    A(const A & b) //默认复制构造函数
    {
    	里面的全部成员变量=b中的全部成员变量 //这里会有指针指向同一位置的浅拷贝问题
    }

顺便提一嘴:如果重载了构造函数,默认构造函数就不存在,要重写
一旦有构造函数,就不能有默认构造函数;一旦有复制构造函数,就不能有默认复制构造函数。复制构造函数也是构造函数。因此,一旦定义了复制构造函数,默认构造函数也不复存在;但只定义构造函数,默认复制构造函数还在。

四种初始化方式

好的,进入正题。
四种初始化方式(注意这里重点谈论的是初始化,和之后阶段的=赋值,重载赋值运算符没有任何关系)
1、A a1; 无参
2、A a1(参数列表); 无参时理论上变成了A a1(); 但事实上A a();初始化对象是不对的,会被认成在声明函数。
3、A a1=A(参数列表); 无参时 A a1=A(); 这个绝对正确,后面会证明,没有调用复制构造函数。但需要一个形式上的复制构造函数,一旦自定义了复制构造函数,且没有加const 这个句子会报错(详见后面代码实验与解释)。证明是编译器进行了优化。
4、A a1(a2); A a1=a2; a2是已经存在的类A的一个对象实例,这个方式和前三种都不一样,调用的是复制构造函数。

A a= A();

主要的问题在第三种方式,有人理解A a1=A();是先生成了一个无名临时对象,再作为初始化调用复制构造函数来初始化a1,理论很美好,事实上并不正确!! 有一定C++基础的人知道复制构造函数的深拷贝浅拷贝问题,如果按照上述理论,如果A中有指针成员变量,直接调用默认复制构造函数会出现问题(譬如析构两次之类)。以下做了一个小实验:

#include<iostream>
using namespace std;
class A
{
public:
    char *a;
    int integer;
    A() //无参构造函数
    {
        a=new char(3);
        a[0]='a';
    }
    A(int n) //有参构造函数
    {
        a=new char(n);
        a[0]='n';
    }
    A(A& b) //消除默认复制构造函数
    {
        a=b.a;
    }
    A(const A & b) //如果注释这个类型的复制构造函数A x=A(参数列表);会报错
    {
        a=new char(3);
        a[0]='c';
        cout<<"copy"<<endl;
    }
    ~A() //自定义析构函数
    {
        a[0]='d';
        cout<<a[0]<<endl;
        delete[] a;
    }
};
int main()
{
    //A z();在定义函数,而非类对象
    A y;
    cout<<y.a[0]<<endl;
    cout<<y.integer<<endl;
    A x=A(); //编译器优化 == A x;
    cout<<x.a[0]<<endl;
    cout<<x.integer<<endl;
    A n=A(5); //编译器优化 == A n(5);
    cout<<n.a[0]<<endl; //这个的integer其实还是随机初始化,我这边正好是0,把n x y换个位置就不是0了
    cout<<n.integer<<endl;
    return 0;
}

得到输出:

a //A y;正常调用无参构造函数生成对象
58 //随机初始化
a //A x=A();经编译器优化相当于A x;
4199705 //随机初始化
n //A x=A(5);经编译器优化相当于A x(5);
0 //其实也是随机初始化,正好是0而已
d //这里才开始析构,前面根本没有临时无名对象
d //析构2
d //析构3

我们在自定义的复制构造函数里,把对象的值固定的改成c,在自定义的析构函数里delete了成员变量a。事实是,A x=A();根本没有生成临时无名对象,调用复制构造函数,并析构。编译器自动优化成了A x;所以再也不用担心A x=A()初始化的时候浅拷贝。另外提一嘴,int没初始化产生奇怪的数字,并不像 https://stackoverflow.com/questions/17690095/c-initialization-of-int-variables-by-an-implicit-constructor/17690411#17690411 里说的A a=A();会自动初始化integer。

总结

总结,经过编译器优化后,A a(参数列表); 和 A a=A(参数列表);没有任何区别。都不经过复制构造函数,后者形式性的需要一个A (const A &a){}的复制构造函数存在(默认复制构造函数就长这样),但又不会经过这个函数。当参数列表为空,即用无参构造函数生成对象,前者写作 A a;不可以写作A a();否则编译器会认为是在声明函数。

最后再次强调,这里的编译器优化只针对初始化,初始化完之后,再进行赋值a1=A(),就会出现刚刚美好的三步理论:1、=右边调用无参构造函数生成无名临时对象 2、调用赋值运算符(如要避免浅拷贝请重载)赋值a1(注意这里不是初始化,和复制构造函数没关系!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值