本文由@呆代待殆原创,转载请注明出处。程序员
对于一个类来讲,咱们把copy constructor、copy-assignment operator、move constructor、move-assignment operator、destructor统称为copy control。数组
今天咱们先来聊聊其中的copy constructor、copy-assignment operator的destructor这三个。函数
copy constructor优化
copy constructor:一个constructor若是他的第一个参数是对类的引用,且其余的参数都有缺省值(default values)则,这是一个copy constructor。spa
1,第一个参数必须是引用类型,由于当咱们把一个object当作参数传递给一个方法的非引用变量的时候会自动调用copy constructor方法,若是copy constructor自身的参数就是非引用类型的话,这个方法就会引发无限递归调用,而后你的程序就boomshakalaka~~。指针
2,通常咱们会把第一个参数设成const,由于通常状况下不会对其进行修改,除非你另有打算。code
3,由于copy constructor在不少状况下是默认调用的,如如下状况,因此通常不会把copy constructor设成explicit。blog
1 std::string s; 2 std::string s1 = s; //隐式调用了copy constructor 3 std::string s2 = std::string(s1);//显式调用了copy constructor
1 class Foo{ 2 public: 3 Foo(const Foo&); 4 //... 5 };
什么时候发生copy constructor调用递归
为了弄清这个问题咱们须要弄清另一组概念:direct initialization 和copy initialization。内存
direct initialization:要求编译器按照通常的方法匹配(function matching)来选择要调用的方法。
copy initialization:要求编译器将右操做数拷贝到左操做数,有必要的话还会进行类型转换,这个过程会调用copy constructor或者move constructor(本文暂不介绍)。
1 std::string s1("balabala"); //direct initialization 2 std::string s2(10, 'a'); //direct initialization 3 std::string s3 = s2; //copy initialization 4 std::string s4 = std::string(s3); //copy initialization 5 std::string s5 = "const char* converts to string";//copy initialization
copy initialization发生的状况以下:
1,用=来初始化定义的变量时。
2,把一个object当作参数传递给一个方法的非引用变量的时。
3,方法返回一个非引用类型的object时。(返回时会首先生成一个临时object)
4,用花括号列表初始化一个数组或聚合类(aggregate class)成员时。
因为编译器带来的误解:
如今的编译器有时候会自动绕过copy constructor即编译器会把下面这一句话
std::Book book = "9-9-9-9";//假设Book是一个自定义的类
换成下面这个
std::Book book("9-9-9-9");
请注意在执行上上面两句语言是彻底不同的,第一句会首先调用Book(const char*)构造函数生成一个临时object而后再调用Book(const Book&)把临时object复制给book。而第二句话会直接调用Book(const char*)而后完事儿。若是你想验证他们的区别能够实现Book类并将Book(const Book&)设置成私有方法(防止编译器自动优化),以后你就会发现第一条语句没法执行了。
Copy-assignment operator
copy-assignment operator:写这个方法就是对=操做符进行重载。
1,copy-assignment operator的返回值通常是对其左操做数(left-hand operand)的引用,这是为了让object的行为更像内置类型而决定的。
1 class Foo{ 2 public: 3 Foo& operator=(const Foo&); 4 //... 5 };
什么时候发生copy-assignment operator调用
答案很显然是用到=操做符的时候啊,可是这里要注意的是
初始化的时候并不会调用copy-assignment operator
初始化的时候并不会调用copy-assignment operator
初始化的时候并不会调用copy-assignment operator
重要的事情说三遍,举例以下
1 std::string s; 2 std::string s1 = s; //对s1进行初始化,调用的是copy constructor 3 s1 = s; //对s1进行赋值,调用的是copy-assignment operator
Destructor
Destructor:destructor有两个部分,function body和destruction part,前者由类的编写者写明须要作的内容,后者是隐式的,不须要程序员关心,在function body执行完后自动执行,会销毁类的非静态数据成员。
1,由于Destructor没有参数,因此它是不能被重载的
1 class Foo{ 2 public: 3 ~Foo(); 4 //... 5 };
什么时候发生Destructor调用
1,当超出object 的做用域(scope)时。
2,容器销毁时(container),里面的元素(element)也会跟着调用自身的destructor从而销毁。
3,人为使用delete的时候。
4,由某个表达式建立的临时变量在这个表达式执行完后将自动调用destructor从而销毁。
5,类的成员若是自身有destructor,会在这个类销毁的时候调用自身的destructor。
关于编译器自动提供的版本(Synthesized)
Synthesized copy constructor:即便咱们提供了其余版本的copy constructor,编译器仍然会提供这个版本的copy constructor给咱们,它会依次复制非静态成员给被建立的object,对数组也能正常工做,对于class类型会调用它们本身的copy constructor。
Synthesized copy-assignment operator:行为和Synthesized copy constructor相似,依次把非静态成员复制给左操做数。
Synthesized destructor:destructor的function body为空。
关于什么时候咱们须要自定义上述的三个方法
1,当须要destructor时,上述三给方法都是须要的。
2,当须要copy constructor时,copy-assignment operator也是须要的,反之亦然。
而当咱们须要删除本身动态分配的内存时,就要用到destructor。
当咱们须要进行深度复制时会用到另外两个,好比对指针指向的元素进行复制等等。
关于delete和default的用法
咱们能够用default显示声明咱们想要用默认版本的copy control,也能够用delete显示声明咱们彻底不须要这类方法来达到禁止这个object进行相关的复制和赋值操做。
1,咱们能delete除了destructor之外的全部方法来达到显示告知这个object不能进行相关操做的目的,delete只能写在一次声明出现的地方。
2,咱们能对全部有默认版本的函数用default显示声明咱们须要这个默认版本,default能够写在方法声明的地方也能够写在方法定义的地方。
1 class Foo{ 2 public: 3 Foo() = default; //显式说明使用默认版本 4 Foo(const Foo&) = delete; //delete copy constructor 5 Foo& operator=(const Foo&) = delete; //delete copy-assignment operator 6 ~Foo() = default; //显式说明使用默认版本 7 void myFuntion() = delete; //delete本身的方法 8 //... 9 };
参考资料:《C++ primer 英文第五版》