【c++】深拷贝和浅拷贝简单理解

本文通过示例介绍了浅拷贝和深拷贝的概念,指出浅拷贝中多个指针共享同一内存可能导致数据不一致性,而深拷贝则是为每个指针创建独立副本以确保数据独立性。在面向对象编程中,浅拷贝在类实例中的问题也被详细阐述,包括类的浅拷贝构造函数和赋值操作可能导致的错误结果和内存管理问题。
摘要由CSDN通过智能技术生成

1.浅拷贝
浅拷贝:多个指针指向同一段内存,出现一处指针修改数据,其它指针的数据也发生改变。

例如:

int main()
{
   char buf[100];
   char* strArr[4];//长度为4的字符指针数组,分别指向4个字符串
   for (int i = 0; i <4 ; i++)//从键盘获取4个字符串
   {
      cin >> buf;
      strArr[i] = buf;
   }
   for (int i = 0; i < 4; i++)//把4个字符串输出到屏幕
      cout << strArr[i] << " ";

   return 0;
}

在键盘中输入4个单词:Apple Banana Orange Peach
但是输出的结果却是:Peach Peach Peach Peach

原因如图:

假设buf的地址为200。第一次从键盘读取Apple,并把200这个地址赋值给strArr[0]。
第二次从键盘读取Banana,并把200这个地址赋值给strArr[1],注意这个地方的问题,由于buf这段内存只有一个,第二次赋值会把第一次的Apple覆盖。
第三次从键盘读取Orange,并把200这个地址赋值给strArr[2],但这时Orange把前面的输入覆盖了。
第四次从键盘读取Pear,并把200这个地址赋值给strArr[3]。但这是Pear把前面的输入覆盖了。
最后输出时strArr[0]到strArr[3]都去地址200处取字符串,就全部都是"Pear"。


这是一个典型的浅拷贝,多个指针指向同一段内存造成的。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

//深拷贝
//下面程序,从键盘获取4个字符串,然后输出到屏幕
int main()
{
    char buf[100];
    char* strArr[4];//长度为4的字符指针数组,分别指向4个字符串
    for (int i = 0; i < 4; i++)//从键盘获取4个字符串
        {
            cin >> buf;
            //strArr[i] = buf;//这是浅拷贝
            //下面的是深拷贝
            strArr[i] = (char*)malloc(strlen(buf) + 1);//1.创建单独的内存
            strcpy(strArr[i],buf);//2.复制数据
        }

    for (int i = 0; i < 4; i++)//把4个字符串输出到屏幕
        cout << strArr[i] << " ";

    //最后需要释放动态内存
    for (int i = 0; i < 4; i++)
        free(strArr[i]);
    return 0;
}

1.2 面向对象的浅拷贝(类)

下面是一个有问题的类(StringBad类的简化版),只是为了演示浅拷贝的问题

class CStr
{
private:
char* str;//字符串指针
public:
CStr(const char* s)//构造函数,通过s构造一个CStr类对象
{
    str = new char[strlen(s) + 1];//动态创建内存
    strcpy(str,s);//把s的内容复制到str
}
void set(const char* s)//修改字符串的值
{
    delete[]str;//释放原来的内存
    str = new char[strlen(s) + 1];//动态创建内存
    strcpy(str, s);//把s的内容复制到str
}
void show()const
{
    cout << str << endl;
}
};

int main()
{
    CStr s1{"有趣的编程"};
    s1.show();//输出s1的内容

    CStr s2 = s1; //把s1赋值给s2
    s2.show();//输出s2的内容

    s2.set("好好学习,天天向上");//修改s2的值
    s2.show();//输出s2的内容
    s1.show();//再输出s1的内容

    return 0;
}

创建一个对象s1,并初始化为"有趣的编程";
创建另一个对象s2,并把s1赋值给s2;
输出s2的内容,这是是s1赋值的值,这是正确的;
修改s2的内容,将其改为"好好学习,天天向上";
输出s2的内容,是修改后的值"好好学习,天天向上",这是正确的;
输出s1的值,应该是"有趣的编程",但实际输出的却是"好好学习,天天向上",这是错误的。
错误的原因也是浅拷贝造成的,具体原因如下图:

2.深拷贝
深拷贝,即为每个指针创建一个单独的内存,然后再复制数据。
2.1 面向过程的深拷贝
把前面的浅拷贝程序进行适当的修改,如下:

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

//深拷贝
//下面程序,从键盘获取4个字符串,然后输出到屏幕
int main()
{
    char buf[100];
    char* strArr[4];//长度为4的字符指针数组,分别指向4个字符串
    for (int i = 0; i < 4; i++)//从键盘获取4个字符串
        {
            cin >> buf;
            //strArr[i] = buf;//这是浅拷贝
            //下面的是深拷贝
            strArr[i] = (char*)malloc(strlen(buf) + 1);//1.创建单独的内存
            strcpy(strArr[i],buf);//2.复制数据
        }

    for (int i = 0; i < 4; i++)//把4个字符串输出到屏幕
        cout << strArr[i] << " ";

    //最后需要释放动态内存
    for (int i = 0; i < 4; i++)
        free(strArr[i]);
    return 0;
}

从上面的程序能够正常的输出。
第12行,为每个字符串创建单独的内存,用于存放数据。
第13行,将数据从buf复制到新创建的内存。
第20,21行,释放每个字符串单独创建的内存,否则出现内存泄漏。

2.2 面向对象的深拷贝(类)
这个过程比较复杂,下面的String类针对浅拷贝的问题,重新实现了拷贝构造函数和赋值=运算符函数。

设计并实现一个动态整型数组类Vect,要求: (1)实现构造函数重载,可以根据指定的元素个数动态创建初始值为0的整型数组,或根据指定的内置整型数组动态创建整型数组。 (2)设计拷贝构造函数和析构函数,注意使用深拷贝。 (3)设计存取指定位置的数组元素的公有成员函数,并进行下标越界,若越界则输出“out of boundary”。 (4)设计获取数组元素个数的公有成员函数。 (5)设计用于输出数组元素的公有成员函数,元素之间以空格分隔,最后以换行符结束。 在main函数中按以下顺序操作: (1)根据内置的静态整型数组{1,2,3,4,5}构造数组对象v1,根据输入的整型数构造数组对象v2。 (2)调用Vect的成员函数依次输出v1和v2的所有元素。 (3)输入指定的下标及对应的整型数,设置数组对象v1的指定元素。 (4)根据数组对象v1拷贝构造数组对象v3。 (5)调用Vect的成员函数依次输出v1和v3的所有元素。 设计并实现一个动态整型数组类Vect,要求: (1)实现构造函数重载,可以根据指定的元素个数动态创建初始值为0的整型数组,或根据指定的内置整型数组动态创建整型数组。 (2)设计拷贝构造函数和析构函数,注意使用深拷贝。 (3)设计存取指定位置的数组元素的公有成员函数,并进行下标越界,若越界则输出“out of boundary”。 (4)设计获取数组元素个数的公有成员函数。 (5)设计用于输出数组元素的公有成员函数,元素之间以空格分隔,最后以换行符结束。 在main函数中按以下顺序操作: (1)根据内置的静态整型数组{1,2,3,4,5}构造数组对象v1,根据输入的整型数构造数组对象v2。 (2)调用Vect的成员函数依次输出v1和v2的所有元素。 (3)输入指定的下标及对应的整型数,设置数组对象v1的指定元素。 (4)根据数组对象v1拷贝构造数组对象v3。 (5)调用Vect的成员函数依次输出v1和v3的所有元素。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值