1,什么是类的拷贝控制
当我们定义一个类的时候,为了让我们定义的类类型像内置类型(char,int,double等)一样好用,我们通常需要考下面几件事:
Q1:用这个类的对象去初始化另一个同类型的对象。
Q2:将这个类的对象赋值给另一个同类型的对象。
Q3:让这个类的对象有生命周期,比如局部对象在部结束的时候,需要销毁这个对象。
因此C++就定义了5种拷贝控制操作,其中2个移动操作是C++11标准新加入的特性:
拷贝构造函数(copy constructor)
移动构造函数(move constructor)
拷贝赋值运算符(copy-assignment operator)
移动赋值运算符(move-assignment operator)
析构函数 (destructor)
前两个构造函数发生在Q1时,中间两个赋值运算符发生在Q2时,而析构函数则负责类对象的销毁。
但是对初学者来说,既是福音也是灾难的是,如果我们没有在定义的类里面定义这些控制操作符,编译器会自动的为我们合成一个版本。这有时候看起来是好事,但是编译器不是万能的,它的行为在很多时候并不是我们想要的。
所以,在实现拷贝控制操作中,最困难的地方是认识到什么时候需要定义这些操作。
2,拷贝构造函数
拷贝构造函数是构造函数之一,它的参数是自身类类型的引用,且如果有其他参数,则任何额外的参数都有默认值。
class Foo{ public: Foo(); Foo(const Foo&); };
我们从上面代码中可以注意到几个问题:
1,我们把形参定义为const类型,虽然我们也可以定义非const的形参,但是这样做基本上没有意义的,因为函数的功能只涉及到成员的复制操作。
2,形参是本身类类型的引用,而且必须是引用类型。为什么呢?
我们知道函数实参与形参之间的值传递,是通过拷贝完成的。那么当我们将该类的对象传递给一个函数的形参时,会调用该类的拷贝构造函数,而拷贝构造函数本身也是一个函数,因为是值传递而不是引用,在调用它的时候也需要调用类的拷贝构造函数(它自身),这样无限循环下去,无法完成。
3,拷贝构造函数通过不是explict的。
如果我们没有定义拷贝构造函数,编译器会为我们定义一个,这个函数会从给定的对象中依次将每个非static成员拷贝到正在创建的对象中。成员自身的类型决定了它是如何被拷贝的:类类型的成员,会使用其拷贝构造函数来拷贝;内置类型则直接拷贝;数组成员会逐元素地拷贝。
区分直接初始化与拷贝初始化:
string name("name_str"); //直接初始化 string name = string("name_str"); // 拷贝初始化 string name = "name_str"; // 拷贝初始化
直接初始化是要求编译器使用普通的函数匹配来选择与我们提供的参数最匹配的构造函数;当我们使用拷贝初始化时,我们要求编译器将右侧运算对象拷贝到正在创建的对象中,如果需要的话还要进行类型转换(第三行代码隐藏了一个C风格字符串转换为string类型)。
3,拷贝赋值运算符
拷贝赋值运算符是一个对赋值运算符的重载函数,它返回左侧运算对象的引用。