C++中直接初始化和拷贝初始化

一、Primer中的说法

首先我们来看看经典是怎么说的:

“当用于类类型对象时,初始化的复制形式和直接形式有所不同:直接初始化直接调用与实参匹配的构造函数,复制初始化总是调用复制构造函数。复制初始化首先使用指定构造函数创建一个临时对象,然后用复制构造函数将那个临时对象复制到正在创建的对象”

还有一段这样说,

“通常直接初始化和复制初始化仅在低级别优化上存在差异,然而,对于不支持复制的类型,或者使用非explicit构造函数的时候,它们有本质区别:

ifstream file1("filename")://直接初始化

ifstream file2 = "filename";//拷贝初始化,但是如果构造函数是非explicit修饰,则编译器会进行优化转成 “file2("filename")”

(注:关键字explicit意思是“明显的”,它修饰于类的构造函数,作用是阻止调用构造函数时的隐式转换。C++类默认是具有隐式转换的,比如构造函数形参是int类型,实参传char,则可以编译通过。)

举例说明:

class ClassTest
{
public:
    ClassTest()
    {
        c[0] = '\0';
        cout << "ClassTest()" << endl;
    }
    ClassTest& operator=(const ClassTest &ct)
    {
        strcpy(c, ct.c);
        cout << "ClassTest& operator=(const ClassTest &ct)" << endl;
        return *this;
    }
    ClassTest(const char *pc)
    {
        strcpy(c, pc);
        cout << "ClassTest (const char *pc)" << endl;
    }
    // private:
    ClassTest(const ClassTest& ct)//注:拷贝构造函数,函数的名称必须和类名称一致,没有返回值,只有唯一的一个参数,而且参数类型是类本身的引用,需要声明成const
    {
        strcpy(c, ct.c);
        cout << "ClassTest(const ClassTest& ct)" << endl;
    }
private:
    char c[256];
};

int main()
{
    cout << "ct1: ";
    ClassTest ct1("ab");//直接初始化
    cout << "ct2: ";
    ClassTest ct2 = "ab";//复制初始化,但编译器进行了优化成为:ct2(“ab”)
    cout << "ct3: ";
    ClassTest ct3 = ct1;//复制初始化,但编译器进行了优化成为:ct3(ct1)
    cout << "ct4: ";
    ClassTest ct4(ct1);//直接初始化
    cout << "ct5: ";
    ClassTest ct5 = ClassTest();//复制初始化
    system("pause");
    return 0;
}

这样的结果是:

接下来我们放开private,看一下结果:

如图所示:ct3、ct4、ct5编译报错。这里可能有些小伙伴有个疑惑:明明对“=”运算符进行了重载,为啥ct3和ct5也不通过?

这里要说明一下,复制初始化(拷贝初始化)和拷贝是有本质区别。初始化是对象创建过程,拷贝(复制)是赋值过程。比如ClassTest c1= c2,这个叫拷贝初始化,调用拷贝构造函数;而c1 =c2则调用的是重载了运算符“=”的函数

我们再做一个实验:对拷贝构造函数进行explicit声明,恢复屏蔽private:

显示ct3报错,再一步证实explicit会阻止编译器对构造函数的优化。

各个语句是否调用复制构造函数,做一个总结:

1、ClassTest ct1("ab");这条语句属于直接初始化,它不需要调用拷贝构造函数,直接调用构造函数ClassTest(const char *pc)。

2、ClassTest ct2 = "ab";这条语句为复制初始化,它首先调用构造函数ClassTest(const char *pc)函数创建一个临时对象,然后调用拷贝构造函数,把这个临时对象作为参数,构造对象ct2。(可能是VS编译器进行了优化,优化后发现最匹配的是ClassTest(const char *pc),并不需要调用过拷贝构造函数)

3、ClassTest ct3 = ct1;这条语句为复制初始化,因为ct1本来已经存在,所以不需要调用相关的构造函数,而直接调用拷贝构造函数,把它值复制给对象ct3;所以当拷贝构造函数变为私有时,该语句不能编译通过。

4、ClassTest ct4(ct1);这条语句为直接初始化,因为ct1本来已经存在,直接调用拷贝构造函数,生成对象ct3的副本对象ct4。所以当拷贝构造函数变为私有时,该语句不能编译通过。

注:第4个对象ct4与第3个对象ct3的创建所调用的函数是一样的,但是本人却认为,调用复制函数的原因却有所不同。因为直接初始化是根据参数来调用构造函数的,如ClassTest ct4(ct1),它是根据括号中的参数(一个本类的对象),来直接确定为调用复制构造函数ClassTest(const ClassTest& ct),这跟函数重载时,会根据函数调用时的参数来调用相应的函数是一个道理;而对于ct3则不同,它的调用并不是像ct4时那样,是根据参数来确定要调用复制构造函数的,它只是因为初始化必然要调用复制构造函数而已。它理应要创建一个临时对象,但只是这个对象却已经存在,所以就省去了这一步,然后直接调用复制构造函数,因为复制初始化必然要调用复制构造函数,所以ct3的创建仍是复制初始化。

5、ClassTest ct5 = ClassTest();这条语句为复制初始化,首先调用默认构造函数产生一个临时对象,然后调用复制构造函数,把这个临时对象作为参数,构造对象ct5。所以当复制构造函数变为私有时,该语句不能编译通过。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值