翻译的是wikipedia关于copy constructor,地址:点击打开链接
拷贝构造函数是C++语言中用一个已有对象创建一个新对象的特殊构造函数,该函数的第一个参数必须是函数所在类型的引用(译注:const/non-const都可以,可以有多个参数剩余参数必须有默认值,一定要是引用,这些原因见后,问:有多个参数拷贝构造如何调用?).
通常编译器会自动为每一个class创建一个拷贝构造函数(显示拷贝构造);有些情况下程序员自定义了拷贝构造函数(用户自定义拷贝构造),这时编译器不合成拷贝构造函数。因此,总是有一个拷贝构造函数要么是用户自定义或者编译器生成。
通常当一个对象内含指针或者非共享的引用(如文件描述符)需要一个用户自定义的拷贝构造函数,这时需要一个用于自定的析构函数和赋值函数(译注:The big three三大件)
定义:
可以通过拷贝构造函数和赋值函数实现拷贝,拷贝构造函数的第一个参数是所属对象的引用(可能是const或者volatile),拷贝构造函数可能有多个参数,但是剩余的参数必须有默认值。下面是合法的关于class X的拷贝构造函数定义:
X(const X& copy_from_me); X(X& copy_from_me); X(volatile X& copy_from_me); X(const volatile X& copy_from_me); X(X& copy_from_me, int = 0); X(const X& copy_from_me, double = 1.0, int = 42); ...尽量使用第一个拷贝构造形式除非有更好的理由使用剩下的拷贝构造函数,第一个和第二个的区别是第一个拷贝构造函数可以接收一个临时对象作为其拷贝源,例如:
X a = X(); // 第一个X(const X& copy_from_me)合法,第二个X(X& copy_from_me)则非法 // 因为第二个想要一个non-const X&来创建a,但是编译器调用默认构造函数创建一个临时对象再利用拷贝构造生成临时对象的一份拷贝,对于有些编译器上述采用第二种拷贝构造函数不会报错,但是这是不可移植的(非标准)另外一个显然的区别是:
const X a; X b = a; // X(const X& copy_from_me)是合法的,X(X& copy_from_me)是不合法的,因为后者希望一个non-const X&
std::auto_ptr拷贝构造函数必须是non-const &: std::auto_ptr<type> a;//译注 std::autor_ptr<type> b=a;//a将所有权转让给b The following are invalid copy拷贝构造函数第一个参数必须是引用:
X a; X b=a;//拷贝构造合法的前提的其第一个参数必须是引用以下是非法的拷贝构造函数,原因是第一个参数不是引用:
X(X copy_from_me); X(const X copy_from_me);因为调用这些构造函数同样需要进行对参数的拷贝,这样会导致无穷递归调用。
T x=a;但是这些情形不能保证必须调用拷贝构造函数,因为c++标准允许编译器针对拷贝进行某些优化,一个典型的例子就是返回值优化(RVO)
X fun(){ X a; ....;//针对a的操作 return a; }编译器优化可能变为:
X _result; void fun(X& _result){ ...; return; }
操作:
对象的赋值有两种方法:
通过表达式显示的赋值Object a; Object b; a = b; // 转化为Object::operator=(const Object&),因此调用A.operator=(B)进行复制,而非拷贝构造
Object b = a; //转化为Object::Object(const Object&)调用拷贝构造函数b.函数传递参数
type function(Object a);c. 函数返回对象
Object a = function();拷贝构造函数只用于初始化,不会应用于赋值(赋值是调用assignment operator)
例子:
以下例子介绍了拷贝构造函数怎么工作及为什么要调用它们:
显示拷贝构造函数:
让我们先看如下例子:
#include <iostream> class Person { public: int age; explicit Person(int a) : age(a) { } }; int main() { Person timmy(10); Person sally(15); Person timmy_clone = timmy; std::cout << timmy.age << " " << sally.age << " " << timmy_clone.age << std::endl;//10 15 10 timmy.age = 23; std::cout << timmy.age << " " << sally.age << " " << timmy_clone.age << std::endl;//23 15 10 }正如预期一样,timmy拷贝进了新对象timmy_clone,当timmy的成员age改变了,timmy_clone的成员age仍保持不变,这是因为它们是完全不同的对象
Person(const Person& other) : age(other.age) // calls the copy constructor of the age,内置类型是operator =直接赋值 { }因此,我们什么时候需要自定义一个拷贝构造函数呢?下一节将介绍
用户自定义拷贝构造函数:
现在,考虑一个非常简单的内含动态数组类像如下:
#include <iostream> class Array { public: int size; int* data; implicit Array(int sz) : size(sz), data(new int[size]) { } ~Array() { delete[] this->data; } }; int main() { Array first(20); first.data[0] = 25; { Array copy = first; std::cout << first.data[0] << " " << copy.data[0] << std::endl; } //25 25 (1) first.data[0] = 10; //segment fault (2) }因为我们没有显示定义一个拷贝构造函数,编译器将会为我们生成一个,生成的构造函数可能像这样:
Array(const Array& other) : size(other.size), data(other.data) {}问题在于这个拷贝构造对指针data执行浅拷贝(仅拷贝指针的内容),这意味着两个对象的成员data都指向同一片内存区域,这不是我们预期的。当程序执行到(1),对象copy的析构函数调用(栈对象的生命周期与其所在的域相关),Array的析构函数删除了copy.data所指向的内存,因为first.data和copy.data都指向的相同内存,所以到(2)时first.data是个空悬指针,存取它导致了段错误。
// for std::copy #include <algorithm> Array(const Array& other) : size(other.size), data(new int[other.size]) { std::copy(other.data, other.data + other.size, data); }这里我们创建了一个新的int数组并从源对象拷贝了元素过来,现在other的析构函数删除的其自己的内存,不是first.data的,此时(2)不会再有段错误。
拷贝构造和模板:
和预期相反,一个模板拷贝构造函数不是用户自定义拷贝构造函数。如下这样定义是不够的:
template <typename A> Array::Array(A const& other) : size(other.size()), data(new int[other.size()]) { std::copy(other.begin(), other.end(), data); }如果模板参数A是一个数组Array,则需要一个用户自定义的专门针对从Array到Array的非模板拷贝构造函数(模板特化)
位拷贝构造:
位拷贝构造函数执行为对象中所有元素简单的variable-by-variable赋值操作,位拷贝构造因此称为从原始对象到新对象的bit-by-bit拷贝。
可以从上图清晰看出位拷贝构造使得原始对象和新对象都指向了同一变量,因此当一个对象的p改变后两个对象都改变
Logical copy constructor产生一份真是的拷贝即使是动态数据结构,如下图:
从上图可以看出一个新的动态成员变量被创建(这里是new)并拷贝了原始对象的值
显示拷贝构造:
显示拷贝构造使用关键字explicit,例如:
explicit X(const X& copy_from_me);这用来阻止函数调用中或copy-initializetion语法产生的拷贝
Copy constructor拷贝构造函数,布布扣,bubuko.com
原文:http://blog.csdn.net/liuxuejiang158blog/article/details/35988753