C++拷贝构造函数

拷贝构造函数

定义:要么只有一个参数,此参数是自身类类型的常引用,要么多个参数,第一个参数为自身类类型的常引用,其他参数都有默认值

class Foo {
public:
    Foo();
    Foo(const  Foo&);  //可以用初始化列表,或在花括号里一一拷贝
};

合成拷贝构造函数

如果没有为类定义一个拷贝构造函数,编译器会自动生成一个合成拷贝构造函数,类自动生成的合成拷贝构造函数执行浅拷贝,依次拷贝每个非 static 成员

直接初始化(无=号)

匹配最佳的构造函数(注意也有可能匹配上拷贝构造函数)

拷贝初始化

使用拷贝初始化的常见四种情况:

  1. =
  2. 函数非引用形参
  3. 函数非引用返回类型
  4. 用花括号列表初始化一个数组中的元素或一个聚合类中的成员

拷贝初始化有时候会使用移动构造函数而非拷贝构造函数【挖坑,后面补例子】

为什么拷贝构造函数自己的参数必须是引用类型?

如果不是引用,则会调用拷贝构造函数,形成拷贝构造函数调用拷贝构造函数的死循环

编译器可以绕过拷贝/移动构造函数

拷贝初始化中,编译器可以选择跳过拷贝/移动构造函数,直接创建对象

string null_book = "9999"; //拷贝初始化

隐式类型转换(转换构造函数):只接受一个实参,或者其他参数都有默认参数

在需要类对象的时候,编译器可以根据已经有的一个参数,调用转换构造函数,自动创建一个(临时)对象

正常来讲,字符串字面值"9999"const char*类型,由于要进行拷贝初始化,=号右边需要一个string对象,所以根据隐式类型转换,在需要string对象的时候,可以调用相应的转换构造函数,自动创建一个临时string对象:

string (const char* s); //from c-string (4)	转换构造函数

有了临时string对象后,按理说要调用拷贝构造函数,完成对null_book的拷贝初始化

然而,这里并没有调用拷贝构造函数,经测试也没有调用移动构造函数,原因是编译器对拷贝初始化表达式进行了改写

string null_book = "9999"; //拷贝初始化

改写为

string null_book("9999"); // 使临时对象成为要创建的对象

按理说被改写后应该属于(调用拷贝构造函数的)直接初始化

但是在利用转换构造函数生成临时string对象后

编译器直接使临时对象成为要创建的对象(编译器优化),所以没有调用拷贝/移动构造函数

其他例子:

class Foo {
public:
    Foo() = default;
    Foo(const Foo& other) :len(other.len) { cout << "拷贝构造函数!" << endl; }  //拷贝构造函数
    Foo(int length) :len(length) { cout << "转换构造函数" << endl; } 		       //转换构造函数
    Foo(Foo&&) { cout << "移动构造函数" << endl; } 							   //移动构造函数
    Foo& temp_f(Foo temp) {
        return *this;
    }
private:
    int len;
};

int main() {
    Foo c = 10; //本身为拷贝初始化,编译器将其改写为下一行的语句,不会调用拷贝/移动构造函数!
    Foo c(10);  //不会调用拷贝/移动构造函数!
    c.temp_f(10); // 虽然形参非引用,但是也不会调用拷贝/移动构造函数!
    return 0;
}

解析一下这个:

c.temp_f(10); // 虽然形参非引用,但是也不会调用拷贝/移动构造函数!

形参非引用,参数10被作为参数传入,先会调用转换构造函数,使10被隐式转换为一个Foo临时对象

注意,这个对象是临时的,临时量具有常量性(const属性)

常量性意味着:

  1. 如果temp_f的形参是左值引用&,将会报错,左值引用不能接受一个const

  2. 如果temp_f的形参是常量引用const&右值引用&&,不会报错,因为是引用了,当然也不会调用拷贝构造函数

  3. 如果temp_f的形参是普通左值

    Foo& temp_f(Foo temp);
    

    普通左值可以接受const值右值,因为这就直接拷贝了,const值右值没有被改变的风险

    例如:

    int i = 42;
    const int ci = i;
    int j = ci;  // ci的常量特征仅仅在执行改变ci的操作时才会发挥作用,一旦拷贝完成,新的对象就和原来的对象没什么关系了
    

    然后按理来说,函数形参非引用,应该是个拷贝初始化,调用拷贝构造函数

    但是没有!因为传给 temp_f 是一个匿名临时对象

    参考解析:https://blog.csdn.net/weicao1990/article/details/81629955

    只有一个对象对另一个同类型的对象进行初始化才会调用拷贝构造函数,但是匿名对象对另一个同类型的对象初始化不会调用拷贝构造函数,因为c++编译器对这种情况进行优化,直接将匿名对象转化为该对象,不需要进行额外的内存分配,提高了效率;

    编译器在此时进行了优化,绕过了拷贝/移动构造函数,将Foo临时对象直接创建了Foo对象作为形参,所以并没有调用拷贝/移动构造函数!

posted @ 2021-11-24 19:01  好爱我的猫  阅读(151)  评论(0)    收藏  举报