一、深拷贝和浅拷贝构造函数总结:
1、两个特殊的构造函数:
(1)无参构造函数:
没有参数的构造函数
Class Test
{
public:
Test()
{
//这是一个无参构造函数
}
};
当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空;换句话来说,就是我们在类中,不用我们程序猿自己写,编译就自动提供了无参构造函数(只是我们肉眼看不到!)
#include <iostream>
#include <string>
class Test{
//编译器默认给我们提供了一个无参构造函数,只是我们肉眼看不到
};
int main()
{
Test t;
return 0;
}
结果输出(编译时能够通过的):
root@txp-virtual-machine:/home/txp# g++ test.cpp
root@txp-virtual-machine:/home/txp#
(2)拷贝构造函数:
参数为const class_name&的构造函数
class Test{
public:
Test(const Test& p)
{
}
}
当类中没有定义拷贝构造函数时,编译器默认提供了一个拷贝构造函数,简单的进行成员变量的值赋值
#include <iostream>
#include <string>
class Test{
private:
int i;
int j;
public:
Test(const Test& p)编译器默认提供这样操作的
{
i = p.i;
j = p.j;
}
};
int main()
{
Test t;
return 0;
}
输出结果(编译可以通过):
root@txp-virtual-machine:/home/txp# g++ test.cpp
root@txp-virtual-machine:/home/txp#
(3)注意:
在写程序的时候,定义的类对象初始化时看属于哪种类型的:
Test t;//对应无参构造函数
Test t(1);//对应有参构造函数
Test t1;
Test t2=t1;//对应拷贝构造函数
比如下面我定义的类对象属于无参构造函数(当然前提是你手写了其他构造函数,虽然说编译器会默认提供,但是既然要手写,那么三种构造函数就在定义类对象的时候按需求来写),如果只写了有参数构造函数,那么编译器就会报错:
#include <iostream>
#include <string>
class Test{
private:
int i;
int j;
public:
Test(int a)
{
i = 9;
j=8;
}
Test(const Test& p)
{
i = p.i;
j = p.j;
}
};
int main()
{
Test t;
return 0;
}
输出结果:
root@txp-virtual-machine:/home/txp# g++ test.cpp
test.cpp: In function ‘int main()’:
test.cpp:25:9: error: no matching function for call to ‘Test::Test()’
Test t;
^
test.cpp:25:9: note: candidates are:
test.cpp:15:3: note: Test::Test(const Test&)
Test(const Test& p)
^
test.cpp:15:3: note: candidate expects 1 argument, 0 provided
test.cpp:10:3: note: Test::Test(int)
Test(int a)
^
test.cpp:10:3: note: candidate expects 1 argument, 0 provided
4、拷贝构造函数的意义:
(1)浅拷贝
拷贝后对象的物理状态相同
(2)深拷贝
拷贝后对象的逻辑状态相同
(3)编译器提供的拷贝构造函数只进行浅拷贝
代码版本一:
#include <stdio.h>
#include <string>
class Test{
private:
int i;
int j;
int *p;
public:
int getI()
{
return i;
}
int getJ()
{
return j;
}
int *getP()
{
return p;
}
Test(int a)
{
i = 2;
j = 3;
p = new int;
*p = a;
}
void free()
{
delete p;
}
};
int main()
{
Test t1(3);//Test t1 3;
Test t2 = t1;
printf("t1.i = %d, t1.j = %d, t1.p = %p", t1.getI(), t1.getJ(), t1.getP());
printf("t2.i = %d, t2.j = %d, t2.p = %p", t2.getI(), t2.getJ(), t2.getP());
t1.free();
t2.free();
return 0;
}
输出结果:
root@txp-virtual-machine:/home/txp# g++ test.cpp
root@txp-virtual-machine:/home/txp# ./a.out
t1.i = 2, t1.j = 3, t1.p = 0x1528010
t2.i = 2, t2.j = 3, t2.p = 0x1528010
*** Error in `./a.out': double free or corruption (fasttop): 0x0000000001528010 ***
Aborted (core dumped)
注解:出现了段错误,仔细分析,我们发现这里释放了堆空间两次(因为我们这里没有调用拷贝构造函数,也就是自己去写拷贝构造函数;所以这种情况是浅拷贝,不能释放两次堆空间):
代码版本二(加上拷贝构造函数):
#include <stdio.h>
#include <string>
class Test{
private:
int i;
int j;
int *p;
public:
int getI()
{
return i;
}
int getJ()
{
return j;
}
int *getP()
{
return p;
}
Test(int a)
{
i = 2;
j = 3;
p = new int;
*p = a;
}
Test(const Test& t)
{
i = t.i;
j = t.j;
p = new int;
*p = *t.p;
}
void free()
{
delete p;
}
};
int main()
{
Test t1(4);
Test t2 = t1;
printf("t1.i = %d, t1.j = %d, t1.p = %p", t1.getI(), t1.getJ(), t1.getP());
printf("t2.i = %d, t2.j = %d, t2.p = %p", t2.getI(), t2.getJ(), t2.getP());
t1.free();
t2.free();
return 0;
}
输出结果:
root@txp-virtual-machine:/home/txp# g++ test.cpp
root@txp-virtual-machine:/home/txp# ./a.out
t1.i = 2, t1.j = 3, t1.p = 0xb0a010
t2.i = 2, t2.j = 3, t2.p = 0xb0a030
注解:从打印的p地址空间来看,就知释放的两个对象的堆空间不同,不再是指向同一堆空间了;同时我们发现浅拷贝只是简单数值上的进行赋值而已;深拷贝不只是简单的值赋值,而是从内存的角度来看,是操作不同的内存。
5、什么时候需要进行深拷贝?
(1)对象中有成员指代了系统中的资源
成员指向了动态内存空间
成员打开了外存中的文件
成员使用了系统中的网络端口
注意:一般来说,自定义拷贝构造函数(也就是我们自己手写的),必然需要实现深拷贝!
二、总结:
C++编译器会默认提供构造函数
无参构造函数用于定义对象的默认初始化状态
拷贝构造函数在创建对象时拷贝对象的状态
对象的拷贝有浅拷贝和深拷贝两种方式。
好了,今天的分享就到这里,如果文章中有错误或者不理解的地方,可以交流互动,一起进步。我是txp,下期见!