六一的部落格


关关难过关关过,前路漫漫亦灿灿。



copy constructor

拷贝初始化时调用

只接受一个参数,是自身类类型的左值引用,或者其他所有参数都有默认值

之所以要求参数为左值引用: 此时不存在拷贝

拷贝源对象到目的对象

如果源对象是左值,匹配拷贝构造函数; 可能会发生一次隐式转换

建议不是explicit的, 允许隐式调用


拷贝与赋值

C++中的拷贝即拷贝初始化: 使用同类型或者兼容类型的另一个对象来初始化该对象

C++的赋值, 有一个前提: 该对象已被创建. 使用新的值替换旧值


拷贝构造函数的两种形式

  1. 形参为具有底层const的左值引用

    1T(const T &);

    通常使用这个版本, 具有底层const的左值引用可以用来绑定:

    • 左值 最优
    • 具有顶层const的左值 非最优
    • 右值 非最优
  2. 形参为左值引用

    1T(T &);

    较少使用此版本. 是拷贝构造函数. 只能接受允许执行写操作的左值


发生拷贝初始化的情形

  1. 定义对象时,使用赋值运算符给出初值
  2. 函数调用时,将对象作为实参传给非引用类型对象,或者返回一个非引用类型对象
  3. 使用insert和push为容器添加成员

拷贝构造函数的匹配优先级

 1#include <iostream>
 2
 3using namespace std;
 4
 5class Foo
 6{
 7public:
 8    Foo(int aa) : a(aa) {}
 9    Foo() : Foo(0) {}
10    Foo(const Foo &f) : Foo(f.a) { cout << "this is const Foo &.\n"; } // 优先匹配 const Foo &
11    Foo(Foo &f) : Foo(f.a) { cout << "this is Foo &.\n"; }             // 优先匹配 Foo &
12
13    void PrintFoo() { cout << a; }
14
15private:
16    int a;
17};
18
19int main()
20{
21    Foo f1(1);
22    const Foo f2(2);
23    Foo f3(f1);
24    Foo f4(f2);
25
26    return 0;
27}

输出

this is Foo &.
this is const Foo &.

左值引用不能绑定具有顶层const的左值, 不能绑定右值

 1#include <iostream>
 2
 3using namespace std;
 4
 5class Foo
 6{
 7public:
 8    Foo(int aa) : a(aa) {}
 9    Foo() : Foo(0) {}
10    // Foo(const Foo &f) : Foo(f.a) { cout << "this is const Foo &.\n"; }
11    Foo(Foo &f) : Foo(f.a) { cout << "this is Foo &.\n"; }
12
13    void PrintFoo() { cout << a; }
14
15private:
16    int a;
17};
18
19int main()
20{
21    Foo f1(1);
22    const Foo f2(2);
23    Foo f3(f1);
24    Foo f4(f2);           // 错误
25    Foo f5(Foo(5));       // 错误
26    return 0;
27}

具有底层const的左值引用可以绑定左值和右值

 1#include <iostream>
 2
 3using namespace std;
 4
 5class Foo
 6{
 7public:
 8    Foo(int aa) : a(aa) { cout << "called delegated constructor: a = " << a << ".\n"; }
 9    Foo() : Foo(0) {}
10    Foo(const Foo &f) : Foo(f.a) { cout << "this is const Foo &: a = " << a << ".\n"; }
11    // Foo(Foo &f) : Foo(f.a) { cout << "this is Foo &.\n"; }
12    // Foo(const Foo &&) = delete;
13    void PrintFoo() { cout << a; }
14
15private:
16    int a;
17};
18
19int main()
20{
21    Foo f1(1);                   // 匹配Foo(int aa)
22    const Foo f2(2);             // 匹配Foo(int aa)
23    Foo f3(f1);                  // 匹配Foo(const Foo &f)
24    Foo f4(f2);                  // 匹配Foo(const Foo &f) 
25    Foo f5(Foo(5));              // 跳过拷贝构造函数, 匹配Foo(int aa)
26    Foo f6 = Foo(6);             // 跳过拷贝构造函数, 匹配Foo(int aa)
27    Foo f7(std::move(Foo(7)));   // 先匹配Foo(int aa), 再匹配Foo(const Foo &f) 
28    return 0;
29}
30
31// 如果使用拷贝构造函数完成拷贝初始化: 在某些情况下, 不是只调用拷贝构造函数; 拷贝构造函数在最后一步调用

输出

called delegated constructor: a = 1.
called delegated constructor: a = 2.
called delegated constructor: a = 1.
this is const Foo &: a = 1.
called delegated constructor: a = 2.
this is const Foo &: a = 2.
called delegated constructor: a = 5.
called delegated constructor: a = 6.
called delegated constructor: a = 7.
called delegated constructor: a = 7.
this is const Foo &: a = 7.

拷贝构造函数的合成版本

synthesized copy constructor

如果没有给出拷贝构造函数的定义, 编译器会提供拷贝构造函数的合成版本

如果拷贝构造函数的合成版本不为删除 delete , 其会将给定对象的每个非static数据成员拷贝到正在构造的对象中

数据成员类型 拷贝方式
类类型 使用类的拷贝构造函数
内置类型 直接拷贝
内置数组 逐元素拷贝

每个数据成员的类型决定了它的拷贝方式


编译器可以略过拷贝/移动构造函数, 但要求其存在且可访问

  1. 不能定义为删除 delete
  2. 非私有成员

拷贝构造函数


copy constructor

拷贝初始化时调用

只接受一个参数,是自身类类型的左值引用,或者其他所有参数都有默认值

之所以要求参数为左值引用: 此时不存在拷贝

拷贝源对象到目的对象

如果源对象是左值,匹配拷贝构造函数; 可能会发生一次隐式转换

建议不是explicit的, 允许隐式调用


拷贝与赋值

C++中的拷贝即拷贝初始化: 使用同类型或者兼容类型的另一个对象来初始化该对象

C++的赋值, 有一个前提: 该对象已被创建. 使用新的值替换旧值


拷贝构造函数的两种形式

  1. 形参为具有底层const的左值引用

    1T(const T &);

    通常使用这个版本, 具有底层const的左值引用可以用来绑定:

    • 左值 最优
    • 具有顶层const的左值 非最优
    • 右值 非最优
  2. 形参为左值引用

    1T(T &);

    较少使用此版本. 是拷贝构造函数. 只能接受允许执行写操作的左值


发生拷贝初始化的情形

  1. 定义对象时,使用赋值运算符给出初值
  2. 函数调用时,将对象作为实参传给非引用类型对象,或者返回一个非引用类型对象
  3. 使用insert和push为容器添加成员

拷贝构造函数的匹配优先级

 1#include <iostream>
 2
 3using namespace std;
 4
 5class Foo
 6{
 7public:
 8    Foo(int aa) : a(aa) {}
 9    Foo() : Foo(0) {}
10    Foo(const Foo &f) : Foo(f.a) { cout << "this is const Foo &.\n"; } // 优先匹配 const Foo &
11    Foo(Foo &f) : Foo(f.a) { cout << "this is Foo &.\n"; }             // 优先匹配 Foo &
12
13    void PrintFoo() { cout << a; }
14
15private:
16    int a;
17};
18
19int main()
20{
21    Foo f1(1);
22    const Foo f2(2);
23    Foo f3(f1);
24    Foo f4(f2);
25
26    return 0;
27}

输出

this is Foo &.
this is const Foo &.

左值引用不能绑定具有顶层const的左值, 不能绑定右值

 1#include <iostream>
 2
 3using namespace std;
 4
 5class Foo
 6{
 7public:
 8    Foo(int aa) : a(aa) {}
 9    Foo() : Foo(0) {}
10    // Foo(const Foo &f) : Foo(f.a) { cout << "this is const Foo &.\n"; }
11    Foo(Foo &f) : Foo(f.a) { cout << "this is Foo &.\n"; }
12
13    void PrintFoo() { cout << a; }
14
15private:
16    int a;
17};
18
19int main()
20{
21    Foo f1(1);
22    const Foo f2(2);
23    Foo f3(f1);
24    Foo f4(f2);           // 错误
25    Foo f5(Foo(5));       // 错误
26    return 0;
27}

具有底层const的左值引用可以绑定左值和右值

 1#include <iostream>
 2
 3using namespace std;
 4
 5class Foo
 6{
 7public:
 8    Foo(int aa) : a(aa) { cout << "called delegated constructor: a = " << a << ".\n"; }
 9    Foo() : Foo(0) {}
10    Foo(const Foo &f) : Foo(f.a) { cout << "this is const Foo &: a = " << a << ".\n"; }
11    // Foo(Foo &f) : Foo(f.a) { cout << "this is Foo &.\n"; }
12    // Foo(const Foo &&) = delete;
13    void PrintFoo() { cout << a; }
14
15private:
16    int a;
17};
18
19int main()
20{
21    Foo f1(1);                   // 匹配Foo(int aa)
22    const Foo f2(2);             // 匹配Foo(int aa)
23    Foo f3(f1);                  // 匹配Foo(const Foo &f)
24    Foo f4(f2);                  // 匹配Foo(const Foo &f) 
25    Foo f5(Foo(5));              // 跳过拷贝构造函数, 匹配Foo(int aa)
26    Foo f6 = Foo(6);             // 跳过拷贝构造函数, 匹配Foo(int aa)
27    Foo f7(std::move(Foo(7)));   // 先匹配Foo(int aa), 再匹配Foo(const Foo &f) 
28    return 0;
29}
30
31// 如果使用拷贝构造函数完成拷贝初始化: 在某些情况下, 不是只调用拷贝构造函数; 拷贝构造函数在最后一步调用

输出

called delegated constructor: a = 1.
called delegated constructor: a = 2.
called delegated constructor: a = 1.
this is const Foo &: a = 1.
called delegated constructor: a = 2.
this is const Foo &: a = 2.
called delegated constructor: a = 5.
called delegated constructor: a = 6.
called delegated constructor: a = 7.
called delegated constructor: a = 7.
this is const Foo &: a = 7.

拷贝构造函数的合成版本

synthesized copy constructor

如果没有给出拷贝构造函数的定义, 编译器会提供拷贝构造函数的合成版本

如果拷贝构造函数的合成版本不为删除 delete , 其会将给定对象的每个非static数据成员拷贝到正在构造的对象中

数据成员类型 拷贝方式
类类型 使用类的拷贝构造函数
内置类型 直接拷贝
内置数组 逐元素拷贝

每个数据成员的类型决定了它的拷贝方式


编译器可以略过拷贝/移动构造函数, 但要求其存在且可访问

  1. 不能定义为删除 delete
  2. 非私有成员