一、拷贝构造函数的性质
定义:
拷贝构造函数是一个成员函数,它用同一个类的一个对象初始化另一个对象。
语法:
-
classname (
const classname &obj) {
-
// body of constructor
-
}
note: 当类中没有定义拷贝构造函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量的值赋值。
拷贝构造函数的作用:
1.从同一类型的另一个对象初始化一个对象。 |
2.将对象拷贝作为一个函数的参数。 |
3.拷贝一个对象以返回它的函数。 |
拷贝构造函数的意义:
浅拷贝 | 拷贝后对象的物理状态相同 |
深拷贝 | 拷贝后对象的逻辑状态相同 |
note:编译器提供的拷贝构造函数只进行浅拷贝。
什么时候调用拷贝构造函数:
1 .当类对象返回一个值; |
2.当类的对象被作为参数通过值传递(到函数)时; |
3.当一个类的对象是基于同一个类的另一个对象构造时; |
4.当编译器生成一个临时对象时 |
Example1:
-
#include <iostream>
-
-
using
namespace
std;
-
-
class Line {
-
-
public:
-
int getLength(void);
-
Line(
int len);
// 普通构造函数
-
Line(
const Line &obj);
// 拷贝构造函数
-
~Line();
// 析构函数
-
-
private:
-
int *ptr;
-
};
-
-
// 成员函数定义,包括构造函数
-
Line::Line(
int len) {
-
cout <<
"普通构造函数分配指针ptr" <<
endl;
-
-
// 给指针ptr分配内存;
-
ptr =
new
int;
-
*ptr = len;
-
}
-
-
Line::Line(
const Line &obj) {
//obj是用于初始化另一个对象的对象的引用。
-
cout <<
"拷贝构造函数分配指针 ptr." <<
endl;
-
ptr =
new
int;
-
*ptr = *obj.ptr;
// 拷贝值
-
}
-
-
Line::~Line(
void) {
-
cout <<
"释放内存!" <<
endl;
-
delete ptr;
-
}
-
-
int Line::getLength(
void) {
-
return *ptr;
-
}
-
-
void display(Line obj) {
-
cout <<
"line 的长度 : " << obj.getLength() <<
endl;
-
}
-
-
// Main 函数实现
-
int main() {
-
Line line(10);
//调用普通构造函数 Line(int len);
-
-
display(line);
//调用拷贝构造函数 Line(const Line &obj);
-
getchar();
-
return
0;
-
}
编译运行,结果如下:
调用拷贝构造函数的另一种方式 Line line2 = line1; 如下:
-
int main() {
-
-
Line line1(10);
-
-
Line line2 = line1;
// 这也是调用拷贝构造函数
-
-
display(line1);
-
display(line2);
-
-
getchar();
-
return
0;
-
}
二、深拷贝与浅拷贝
Example2:
-
#include <stdio.h>
-
-
class Test
-
{
-
private:
-
int i;
-
int j;
-
int* p;
-
public:
-
int getI()
-
{
-
return i;
-
}
-
int getJ()
-
{
-
return j;
-
}
-
int* getP()
-
{
-
return p;
-
}
-
Test(
const Test& t)
//拷贝构造函数
-
{
-
i = t.i;
-
j = t.j;
-
p =
new
int;
-
-
*p = *t.p;
-
}
-
Test(
int v)
-
{
-
i =
1;
-
j =
2;
-
p =
new
int;
-
-
*p = v;
-
}
-
void free()
-
{
-
delete p;
-
}
-
};
-
-
int main()
-
{
-
Test t1(3);
//调用构造函数Test(int v)
-
Test t2(t1);
//调用拷贝构造函数Test(const Test& t)
-
-
printf(
"t1.i = %d, t1.j = %d, *t1.p = %d\n", t1.getI(), t1.getJ(), t1.getP());
//*t1.p = 4887464
-
printf(
"t2.i = %d, t2.j = %d, *t2.p = %d\n", t2.getI(), t2.getJ(), t2.getP());
//*t2.p = 4887512
-
-
t1.
free();
-
t2.
free();
-
getchar();
-
-
return
0;
-
}
编译运行,
可见,t1 和 t2所指的地址空间是不同的 ,因为t2进行深拷贝。
我们不妨把注释掉下列代码:
-
Test(
const Test& t)
//拷贝构造函数
-
{
-
i = t.i;
-
j = t.j;
-
p =
new
int;
-
-
*p = *t.p;
-
}
然后编译运行:
可见,指针 t1 和 t2的地址相同,说明了t1 和t2 都指向了同一片内存空间。
问题分析:
①浅拷贝:默认构造函数只做浅拷贝。
t1 和t2 都指向了同一片内存空间:
当释放内存地址时,t1 和 t2释放的是同一块内存地址,此时会发生编译错误:
②深拷贝:只能与用户定义的复制构造函数。在用户定义的复制构造函数中,我们确保复制对象指向新的内存位置的指针(或引用)。
可见,Bag2 深拷贝 Bag1,Bag2指向了一片新的内存地址。
Question: 什么时候需要进行深拷贝?
当对象中有成员指代了系统中的资源:
1.成员指向了动态内存空间 |
2.成员打开了外存中的文件 |
3.成员使用了系统中的网络端口 |
一般性原则:
自定义拷贝构造函数,必然需要实现深拷贝!
三、拷贝构造函数 VS 赋值操作符
下面的两个语句哪个调用拷贝构造函数,哪个调用赋值操作符?
-
-
MyClass t1, t2;
-
MyClass t3 = t1;
// ----> (1)
-
t2 = t1;
// -----> (2)
答:
当一个已经初始化的对象从另一个现有对象分配一个新值时,调用赋值操作符:
t2 = t1;
//调用赋值操作符, 相当于 "t2.operator=(t1);"
当新对象从现有对象创建时,就会调用拷贝构造函数,作为现有对象的拷贝:
Test t3 = t1;
// 调用拷贝构造函数, 相当于"Test t3(t1);"
Test:
-
-
#include<iostream>
-
#include<stdio.h>
-
-
using
namespace
std;
-
-
class Test
-
{
-
public:
-
Test() {}
-
Test(
const Test &t)
-
{
-
cout <<
"调用拷贝构造函数 " <<
endl;
-
}
-
Test&
operator = (
const Test &t)
-
{
-
cout <<
"调用赋值操作符 " <<
endl;
-
}
-
};
-
-
int main()
-
{
-
Test t1, t2;
-
t2 = t1;
//调用赋值操作符
-
Test t3 = t1;
//调用赋值操作符
-
getchar();
-
return
0;
-
}
编译运行,结果如下:
编译运行,结果如下:
<本文完>
Reference: