关于C++拷贝构造函数,一直不是很明白,于是强迫症又发作,一定要搞懂它!!
另外附上参考的文章(这位大神写得实在太棒了,让我瞬间搞懂了这个纠结了很久的问题):
http://blog.youkuaiyun.com/lwbeyond/article/details/6202256/
一.简介:
拷贝构造函数,又称复制构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其唯一的形参必须是引用,但并不限制为const,一般普遍的会加上const限制。此函数经常用在函数调用时用户定义类型的值传递及返回。拷贝构造函数要调用基类的拷贝构造函数和成员函数。如果可以的话,它将用常量方式调用,另外,也可以用非常量方式调用。
二.自动生成拷贝构造函数:
// C++Test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
class CopyTest
{
private:
string name;
int id;
public:
CopyTest(int i, string n):id(i),name(n)
{
cout<<"Construct!"<<endl;
}
~CopyTest()
{
cout<<"Destruct!"<<endl;
}
void Display()
{
cout<<name<<" "<<id<<endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
CopyTest t1(1, "t1");
CopyTest t2 = t1;
t2.Display();
system("pause");
return 0;
}
结果如下:
Construct!
t1 1
请按任意键继续. . .
Q&A
1.咦?这里木有写那个传说中的拷贝构造函数啊!为什么还是实现了拷贝呢?
原因是如果我们不写,编译器会为我们自动生成一个拷贝构造函数(类似构造函数和析构函数,如果不写,编译器也会为我们自动生成)。
2.那既然会为我们生成,我们为什么还要写拷贝构造函数呢?
因为自动生成的拷贝构造函数只能实现浅拷贝,不能进行深拷贝。关于浅拷贝和深拷贝在下面具体写。简单的说,就是系统自带的一些数据类型可以实现拷贝,而一些复杂的,指针等,不能进行拷贝,这样自动生成的拷贝构造函数肯定不能满足我们的要求,所以我们就要自己写一个拷贝构造函数啦!
三.我们自己的拷贝构造函数:
// C++Test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
class CopyTest
{
private:
string name;
int id;
public:
CopyTest(int i, string n):id(i),name(n)
{
cout<<"Construct!"<<endl;
}
~CopyTest()
{
cout<<"Destruct!"<<endl;
}
//拷贝构造函数
CopyTest(const CopyTest& e)
{
name = e.name;
id = e.id;
cout<<"Copy Construct!"<<endl;
}
void Display()
{
cout<<name<<" "<<id<<endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
CopyTest t1(1, "t1");
CopyTest t2 = t1;//或者CopyTest t2(t1);
t2.Display();
system("pause");
return 0;
}
结果:
Copy Construct!
t1 1
请按任意键继续. . .
四.什么时候会调用拷贝构造函数
CopyTest t2 = t1;
//或者CopyTest t2(t1);
通过=赋值时,或者直接按照拷贝构造函数的定义方式来调用,将一个对象的引用传递给拷贝构造函数,就可以通过拷贝的方式生成一个新对象。
// C++Test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
class CopyTest
{
private:
string name;
int id;
public:
CopyTest(int i, string n):id(i),name(n)
{
cout<<"Construct!"<<endl;
}
~CopyTest()
{
cout<<"Destruct!"<<endl;
}
//拷贝构造函数
CopyTest(CopyTest& e)
{
name = e.name;
id = e.id;
cout<<"Copy Construct!"<<endl;
}
void Display()
{
cout<<name<<" "<<id<<endl;
}
};
//值传递方式传递参数的函数
void PassByValue(CopyTest e)
{
cout<<"Function is called!"<<endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
CopyTest t1(1, "t1");
PassByValue(t1);
system("pause");
return 0;
}
结果:
Copy Construct!
Function is called!
Destruct!
请按任意键继续. . .
// C++Test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
class CopyTest
{
private:
string name;
int id;
public:
CopyTest(int i, string n):id(i),name(n)
{
cout<<"Construct!"<<endl;
}
~CopyTest()
{
cout<<"No"<<id<<" "<<"Destruct!"<<endl;
}
//拷贝构造函数
CopyTest(CopyTest& e)
{
name = e.name;
//为了更好的分辨出哪个是原对象哪个是拷贝出来的对象,这里做了一点小手脚,拷贝时id会+1
id = e.id + 1;
cout<<"Copy Construct!"<<endl;
}
void Display()
{
cout<<name<<" "<<id<<endl;
}
};
//值传递方式从函数中返回
CopyTest ReturnByValue()
{
CopyTest t(1, "test");
return t;
}
int _tmain(int argc, _TCHAR* argv[])
{
ReturnByValue();
system("pause");
return 0;
}
结果:
Copy Construct!
No1 Destruct!
No2 Destruct!
请按任意键继续. . .
五.右值引用与转移语义:
// C++Test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
class CopyTest
{
private:
string name;
int id;
public:
CopyTest(int i, string n):id(i),name(n)
{
cout<<"Construct!"<<endl;
}
~CopyTest()
{
cout<<"No"<<id<<" "<<"Destruct!"<<endl;
}
//拷贝构造函数
CopyTest(CopyTest& e)
{
name = e.name;
id = e.id + 1;//为了更好的分辨出哪个是原对象,这里做了一点小手脚,拷贝出来的对象id+1
cout<<"Copy Construct!"<<endl;
}
void Display()
{
cout<<name<<" "<<id<<endl;
}
};
//值传递方式传递参数的函数
void PassByValue(CopyTest e)
{
cout<<"Function is called!"<<endl;
}
//值传递方式从函数中返回
CopyTest ReturnByValue()
{
CopyTest t(1, "test");
return t;
}
int _tmain(int argc, _TCHAR* argv[])
{
CopyTest t = ReturnByValue();
cout<<"--------------------------------------"<<endl;
t = ReturnByValue();
system("pause");
return 0;
}
Copy Construct!
No1 Destruct!
--------------------------------------
Construct!
Copy Construct!
No1 Destruct!
No2 Destruct!
请按任意键继续. . .
六.深拷贝和浅拷贝
// C++Test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
class CopyTest
{
private:
string name;
int id;
int* pointer;
public:
CopyTest(int i, string n):id(i),name(n)
{
pointer = new int(1);
cout<<"Construct!"<<endl;
}
~CopyTest()
{
delete pointer;
cout<<"No"<<id<<" "<<"Destruct!"<<endl;
}
void Display()
{
cout<<name<<" "<<id<<endl;
}
};
void TestFunc()
{
//函数执行完后,会进行析构操作
CopyTest t1(1, "test");
CopyTest t2(t1);
}
int _tmain(int argc, _TCHAR* argv[])
{
TestFunc();
system("pause");
return 0;
}
结果:
// C++Test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
class CopyTest
{
private:
string name;
int id;
int* pointer;
public:
CopyTest(int i, string n):id(i),name(n)
{
pointer = new int(1);
cout<<"Construct!"<<endl;
}
~CopyTest()
{
delete pointer;
cout<<"No"<<id<<" "<<"Destruct!"<<endl;
}
//拷贝构造函数
CopyTest(CopyTest& e)
{
name = e.name;
id = e.id + 1;//为了便于区分,这里把拷贝构造函数做了一点手脚,拷贝出来的东东id+1
pointer = new int();//重新分配一块内存
*pointer = *(e.pointer);//给该内存赋值,与原对象值相同
cout<<"Copy Construct!"<<endl;
}
void Display()
{
cout<<name<<" "<<id<<endl;
}
};
void TestFunc()
{
//函数执行完后,会进行析构操作
CopyTest t1(1, "test");
CopyTest t2(t1);
}
int _tmain(int argc, _TCHAR* argv[])
{
TestFunc();
system("pause");
return 0;
}
Copy Construct!
No2 Destruct!
No1 Destruct!
请按任意键继续. . .
七.防止按值传递
// C++Test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
class CopyTest
{
private:
string name;
int id;
int* pointer;
//私有的拷贝构造函数
CopyTest(CopyTest& e);
public:
CopyTest(int i, string n):id(i),name(n)
{
pointer = new int(1);
cout<<"Construct!"<<endl;
}
~CopyTest()
{
delete pointer;
cout<<"No"<<id<<" "<<"Destruct!"<<endl;
}
void Display()
{
cout<<name<<" "<<id<<endl;
}
};
void TestFunc()
{
//函数执行完后,会进行析构操作
CopyTest t1(1, "test");
CopyTest t2(t1);
}
int _tmain(int argc, _TCHAR* argv[])
{
TestFunc();
system("pause");
return 0;
}
这样,我们编译一下:
八.几个注意的地方:
X::X(const X&);
X::X(X);
X::X(X&, int a=1);
X::X(X&, int a=1, int b=2);
解答:对于一个类X, 如果一个构造函数的第一个参数是下列之一:
a) X&
b) const X&
c) volatile X&
d) const volatile X&
且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数.
X::X(const X&); //是拷贝构造函数
X::X(X&, int=1); //是拷贝构造函数
X::X(X&, int a=1, int b=2); //当然也是拷贝构造函数
2. 一个类中可以存在多于一个的拷贝构造函数吗?
解答:类中可以存在超过一个拷贝构造函数。
class X {
public:
X(const X&); // const 的拷贝构造
X(X&); // 非const的拷贝构造
};
注意,如果一个类中只存在一个参数为 X& 的拷贝构造函数,那么就不能使用const X或volatile X的对象实行拷贝初始化.
class X {
public:
X();
X(X&);
};
const X cx;
X x = cx; // error
如果一个类中没有定义拷贝构造函数,那么编译器会自动产生一个默认的拷贝构造函数。
这个默认的参数可能为 X::X(const X&)或 X::X(X&),由编译器根据上下文决定选择哪一个。