欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程语言 > c/c++ >内容正文

c/c++

C++中的深拷贝和浅拷贝(详解)

发布时间:2025/3/15 c/c++ 35 豆豆
生活随笔 收集整理的这篇文章主要介绍了 C++中的深拷贝和浅拷贝(详解) 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

2020-07-13

拷贝构造函数是一种特殊的构造函数,在创建对象时,它是使用同一类中之前创建过的对象来初始化新创建的对象。如果没有自定义拷贝构造函数,系统会提供一个缺省的拷贝构造函数,缺省的拷贝构造函数对于基本类型的成员变量,按字节复制,对于类类型成员变量,调用其相应类型的拷贝构造函数。

我们在编写程序的过程中,如果不主动编写拷贝构造函数和赋值函数,编译器将会调用默认的函数,如果类中含有指针变量,那么如果使用的默认的函数就会有错误,下面首先我们先进行简单的介绍,之后再用具体的例子来加以说明。

1.拷贝构造函数和赋值函数

拷贝构造函数,顾名思义,它是一个构造函数,所以它是在对象创建的时候被主动调用的函数,可以将 另外一个对象的变量拷贝给当前对象。赋值函数,是在对象已经存在的情况下才会进行调用。 #include <iostream> #include <set> using namespace std;class Test {public:Test() { // 默认构造函数cout << "Test()" << endl;};Test(int v) :value(v) { // 带参数的构造函数cout << "Test(int v)" << endl;}Test(const Test& obj){ //拷贝构造函数cout << "Test(const Test& obj)" << endl;}Test& operator=(const Test& obj){cout << "Test& operator=(const Test& obj)" << endl;return *this;}~Test() { // 析构函数}private:int value; };int main() {Test a(1);Test c = a;Test d;d = a;getchar();return 0; }


(1)拷贝构造函数和赋值函数

Test c = a; d = a;

这里第一个"=“时对象c还没有存在,所以这里调用的是拷贝构造函数,第二个”="时对象d已经存在了,所以调用的是赋值函数

(2)拷贝构造函数
拷贝构造函数是其它构造函数的重载函数,它的参数是const对象的引用,const比较容易理解,我们将一个对象拷贝给另外一个对象,那该对象的值我们是不希望被改变了的,另外一个原因是,添加 const 限制后,就可以将 const 对象和非 const 对象传递给形参了,因为非 const 类型可以转换为 const 类型,如果没有 const 限制,就不能将 const 对象传递给形参,因为 const 类型不能转换为非 const 类型,这就意味着,不能使用 const 对象来初始化当前对象了;那么这里我们为什么选择传递对象的引用作为函数的参数呢?一个原因是引用传参的时候,形参是实参的一个别名,也就是说它们俩其实是相同的,但是如果我们不传递引用的话,在进入函数的时候,会另外分配一块存储空间给形参使用,形参将会初始化实参的值,在函数调用结束的时候,这块存储空间将会被释放掉,如果说对象比较大的话,在这个初始化的过程中将会比较的耗时;还有一个十分重要的原因是我们在调用拷贝构造函数的时候,如果传递的是对象的话,由于要将实参的值赋给形参,将会调用拷贝构造函数进行赋值,那么就会形成一个死循环,如果您将上述拷贝构造函数中的"&"删除掉,程序将会报错。

(3)赋值函数
赋值函数用到的是运算符的重载,它的返回值是对象的引用,参数是const对象的引用。对于返回值而言,我们希望保留运算符原有的特性,考虑到a=b=c,这个赋值语句的顺序应该是a=(b=c),所以赋值函数重载函数的返回值应该是类的对象,返回对象的引用是因为如果返回对象的话,将会把原先对象的值赋值给临时的对象,这样还会调用一次拷贝构造函数。赋值函数的参数是对象的引用,一个原因是节省时间,另外一个原因是使函数可以传递const的对象。

#include <iostream> using namespace std;class Test { public:Test() { // 默认构造函数cout << "Test()" << endl;};Test(int v) :value(v) { // 带参数的构造函数cout << "Test(int v)" << endl;}Test(const Test& obj){ //拷贝构造函数cout << "Test(const Test& obj)" << endl;}Test operator=(const Test& obj){cout << "Test& operator=(const Test& obj)" << endl;return *this;}~Test() { // 析构函数}private:int value; };int main() {Test a(1);Test c = a;Test d;d = c = a;getchar();return 0; }


由于赋值函数的返回值是对象,这里在函数返回时就调用了拷贝构造函数。

2.何时调用拷贝构造函数

(1)定义一个对象时,以本类另一个对象作为初始值,发生拷贝构造。 (2)如果函数的形参是类的对象,调用函数时,将使用实参初始化形参,发生拷贝构造。 (3)如果函数的返回值是类的对象,函数执行完成返回主调函数时,将使用return语句中的对象初始化 一个临时的无名对象,传递给主调函数,发生拷贝构造。

3.浅拷贝和深拷贝

浅拷贝:如果用默认的拷贝构造函数(赋值函数)去赋值有指针类型的成员变量的对象,将会使两个对象 的指针地址也是一样的,也就是说这两个对象的指针成员变量指向的是相同的地址。 深拷贝:每个对象拥有自己的资源,此时需要显示提供拷贝构造函数和赋值函数。


如果调用了默认的拷贝构造函数(赋值函数),在拷贝过程中是按字节复制的,对于指针型成员变量只复制指针本身,而不复制指针所指向的目标,这将会使同一指针指向相同的区域,同时也会导致同一块资源被释放多次,从而造成错误。
下面的例子是显示定义的赋值函数。

#include <iostream> #include <string.h> using namespace std;class Test { public:Test():ptr(new char[1]) { // 带参数的构造函数cout << "Test():ptr(new char[1])" << endl;}Test operator=(const Test& obj){if (this == &obj) { // s=sreturn *this;}delete[] ptr;ptr = new char[strlen(obj.ptr) + 1];strcpy(ptr, obj.ptr);cout << "Test& operator=(const Test& obj)" << endl;return *this;}Test& operator=(const char *s){delete[] ptr;ptr = new char[strlen(s) + 1];strcpy(ptr, s);cout << "Test& operator=(const Test& obj)" << endl;return *this;}~Test() { // 析构函数delete[] ptr;} private:char* ptr; };int main() {Test a;a= "test";Test b;b= a;Test d;getchar();return 0; }

总结

以上是生活随笔为你收集整理的C++中的深拷贝和浅拷贝(详解)的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。