常见问题
Q1. 下面代码的输出结果是( )?
1 #include <iostream> 2 using namespace std; 3 4 class MyClass 5 { 6 public: 7 MyClass(int n) { number = n; } 8 MyClass(const MyClass &other) 9 { 10 number = other.number; 11 cout << "a "; 12 } 13 private: 14 int number; 15 }; 16 17 void fun(MyClass p) 18 { 19 MyClass temp(p); 20 } 21 22 int main(void) 23 { 24 MyClass obj1(10), obj2(0); 25 MyClass obj3(obj1); 26 fun(obj3); 27 return 0; 28 }
A. a a a B. a a a a C. a a D. a
Q2. 为什么拷贝构造函数的参数是一个引用,可以不是引用吗?
Q3. 什么是深复制?什么是浅复制?
复制构造函数
1. 复制构造函数
只有单个形参,而且该形参是对本类类型对象的引用(常用const修饰),这样的构造函数称为复制构造函数(或拷贝构造函数)。与默认构造函数一样,复制构造函数可由编译器隐式调用。
复制构造函数、赋值操作符和析构函数总称为复制控制。编译器自动实现这些操作,但类也可以定义自己的版本。
如果类需要析构函数,则它也需要赋值操作符和复制构造函数,这是一个有用的经验法则。这个规则常称为三法则,指的是如果需要析构函数,则需要所有三个复制控制成员。
通常,编译器合成的复制控制函数是非常精练的——它们只做必需的工作。但对某些类而言,依赖于默认定义会导致灾难。实现复制控制最困难的部分,往往在于识别何时需要覆盖默认版本。有一种特别常见的情况需要定义自己的复制控制成员:类具有指针成员。
2. 复制构造函数的作用
(1) 根据另一个同类型的对象初始化一个对象
C++支持两种初始化形式:直接初始化和复制初始化。复制初始化使用=符号,而直接初始化将初始化式放在圆括号中。
1 string null_book1("9-999-99999-9"); // 直接初始化 2 string null_book2(null_book1); // 复制初始化 3 string null_book3 = null_book1; // 复制初始化 4 string null_book4 = "9-999-99999-9"; // 复制初始化
上述代码中,语句4为复制初始化,首先使用指定构造函数(接受一个C风格字符串形参的string构造函数)创建一个临时对象,然后用string复制构造函数将那个临时对象复制到正在创建的对象。
但需要注意的是,很多编译器中,语句4的方式等价于语句1的方式。也就是说这些编译器做了优化,跳过了复制构造函数直接调用到普通的构造函数创建对象。
注意下面的语句3不是复制构造函数:
1 string null_book1("9-999-99999-9"); 2 string null_book2; 3 null_book2 = null_book1;
语句2是调用string的默认构造函数创建一个空字符串对象,语句3是利用赋值运算符将null_book1赋值给null_book2。
(2) 复制一个对象,将它作为实参传给一个函数或从函数返回时复制一个对象
(3) 初始化顺序容器中的元素
(4) 根据元素初始化式列表初始化数组元素
3. 浅复制与深复制