C++深复制和浅复制讲解

C++中的复制操作包含深复制和浅复制两种方式。简单来说,浅复制只复制指针而不复制指针指向的内存空间,而深复制会复制指针和指针指向的内存空间。

一般情况下,我们需要使用深复制,以避免浅复制造成指针指向错误的情况。

深复制的实现方式

在C++中,可以通过使用拷贝构造函数和赋值操作符实现深复制。

拷贝构造函数

拷贝构造函数是一种特殊的构造函数,用于在创建对象时,用已存在的对象来初始化新创建的对象。在深复制时,需要在新的对象中重新分配空间,将原始对象的值复制到新的对象中。

以下是一个使用拷贝构造函数实现深复制的示例代码:

#include <iostream>

class MyString{
private:
    char *str;
    int len;
public:
    MyString(const char *s){
        len = strlen(s);
        str = new char[len+1];
        strcpy(str, s);
    }
    // 拷贝构造函数
    MyString(const MyString &src){
        len = src.len;
        str = new char[len+1];
        strcpy(str, src.str);
    }
    ~MyString(){
        delete[] str;
    }
    char* getString(){
        return str;
    }
};

int main(){
    MyString str1("Hello World!");
    std::cout << "str1: " << str1.getString() << std::endl;

    MyString str2 = str1; // 拷贝构造函数
    std::cout << "str2: " << str2.getString() << std::endl;

    return 0;
}

在以上示例中,我们通过拷贝构造函数实现了深复制。我们创建了一个MyString对象str1,并将其值设置为"Hello World!"。接着,我们通过MyString str2 = str1;语句创建了另一个MyString对象str2。C++编译器默认调用拷贝构造函数来进行对象的拷贝操作。在拷贝构造函数中,我们重新分配了内存空间,并将原始对象的值复制到了新的对象中。

赋值操作符

C++中的赋值操作符用于将一个对象的值赋给另一个对象。在深复制时,我们需要在新的对象中重新分配空间,并将原始对象的值复制到新的对象中。为了实现深复制,我们需要自定义赋值操作符,而不是仅仅使用编译器默认的赋值操作符。

以下是一个使用自定义赋值操作符实现深复制的示例代码:

#include <iostream>

class MyString{
private:
    char *str;
    int len;
public:
    MyString(const char *s){
        len = strlen(s);
        str = new char[len+1];
        strcpy(str, s);
    }
    MyString &operator=(const MyString &src){
        if (this == &src)  // 检查自我赋值情况
            return *this;

        delete [] str;    // 释放原有内存
        len = src.len;
        str = new char [len+1];
        strcpy(str, src.str);

        return *this;    // 返回对象的引用
    }
    ~MyString(){
        delete[] str;
    }
    char* getString(){
        return str;
    }
};

int main(){
    MyString str1("Hello World!");
    std::cout << "str1: " << str1.getString() << std::endl;

    MyString str2("Goodbye World!");
    std::cout << "str2: " << str2.getString() << std::endl;

    str2 = str1; // 赋值操作符
    std::cout << "str2: " << str2.getString() << std::endl;

    return 0;
}

在以上示例中,我们定义了一个MyString类的赋值操作符,用于实现深复制。该赋值操作符使用了与拷贝构造函数相同的实现方式,重新分配了内存空间,并将原始对象的值复制到新的对象中。和拷贝构造函数一样,我们不能忘记在对象生命周期结束时释放所占用的内存,否则可能会导致内存泄漏或其他不可预测的行为。

浅复制的实现方式

浅复制只复制指针,而不复制指针所指向的内存空间。由于浅复制没有重新分配内存,当原始对象和新对象都指向同一块内存空间时,就可能出现指向错误的情况。通常情况下,我们不想使用浅复制。然而,在某些情况下,浅复制可以作为一种优化手段来使用,例如,当我们操作大型数据类型时,使用浅复制可以减少操作的开销。

以下是一个使用浅复制实现的示例代码:

#include <iostream>

class MyIntArray{
private:
    int *array;
    int size;
public:
    MyIntArray(int s=0): size(s){
        array = new int[size];
    }
    MyIntArray(const MyIntArray &src){
        size = src.size;
        array = src.array; // 浅复制
    }
    ~MyIntArray(){
        delete [] array;
    }
    int& operator[](int i){
        return array[i];
    }
};

int main(){
    MyIntArray arr1(5);
    arr1[0] = 1;
    arr1[1] = 2;
    arr1[2] = 3;
    arr1[3] = 4;
    arr1[4] = 5;

    MyIntArray arr2 = arr1; // 浅复制
    arr2[4] = 10;

    std::cout << "arr1[4]: " << arr1[4] << std::endl;
    std::cout << "arr2[4]: " << arr2[4] << std::endl;

    return 0;
}

在以上示例中,我们定义了一个MyIntArray类,该类包含一个整型数组和大小。我们在构造函数中分配了内存空间来存储整型数组。在拷贝构造函数中,我们使用了浅复制来实现复制对象的操作。在上面示例中,我们创建了一个MyIntArray对象arr1,将其大小设置为5,并分配内存空间来存储5个整数。接着,我们将arr1的前四个元素分别设置为"1"、"2"、"3"、"4",最后一个元素设置为"5"。之后,我们创建了另一个MyIntArray对象arr2,将其初始化为arr1的一个副本。当我们修改arr2的最后一个元素时,arr1的最后一个元素也会被修改。这是因为浅复制只复制了指针,arr1arr2指向的是同一块内存空间。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++深复制和浅复制讲解 - Python技术站

(0)
上一篇 2023年5月23日
下一篇 2023年5月23日

相关文章

  • 邻接表无向图的Java语言实现完整源码

    如果要实现一个邻接表无向图的Java程序,需要进行以下几个步骤: 1. 定义节点类 首先定义一个节点类来存储图中的每个节点以及它们之间的关系(边): class Node { int label; // 节点编号 List<Node> edges = new LinkedList<>(); // 存储与该节点相连的边 Node(int…

    C 2023年5月22日
    00
  • Windows 2003 服务器安全设置图文教程

    针对“Windows 2003 服务器安全设置图文教程”的完整攻略,我给出如下的详细讲解。 Windows 2003 服务器安全设置图文教程攻略 为什么需要进行安全设置 Windows 2003服务器上的安全设置非常重要,它无论是对个人用户,还是企业用户,都拥有不可忽视的重要性。 首先,Windows 2003服务器安全设置可以保障服务器的安全稳定性,避免网…

    C 2023年5月22日
    00
  • vue实现导入json解析成动态el-table树表格

    首先,我们需要导入所需的依赖。可以使用npm或者yarn命令安装相关依赖: npm install vue vue-router axios element-ui –save-dev 其中,vue是Vue.js框架核心库,vue-router用于路由管理,axios用于发起网络请求,element-ui用于构建UI组件。 接着,我们需要在Vue.js应用中…

    C 2023年5月23日
    00
  • 原生js调用json方法总结

    当我们需要使用JSON格式的数据时,使用JavaScript原生的JSON API来处理数据是非常常见的。在本篇文档中,我们将会全面介绍如何原生JS调用JSON方法。 JSON简介 JSON (JavaScript对象表示法) 是一种用于将数据存储和交换的文本格式。JSON 派生自JavaScript语言,但是JSON 格式是语言无关的。 JSON是一种非常…

    C 2023年5月23日
    00
  • 使用批处理文件异地备份数据库(最近几天的数据)

    下面是使用批处理文件异地备份数据库(最近几天的数据)的完整攻略: 第一步:编写批处理文件 打开文本编辑器,新建一个批处理文件,后缀名为“.bat”。比如,我们可以用“backup.bat”来命名这个文件。 在批处理文件中输入以下代码: @echo off REM 配置数据库备份参数 set backup_path=D:\Backup\Database set…

    C 2023年5月22日
    00
  • C语言使用指针的一维数组

    下面就是关于C语言使用指针的一维数组的使用攻略: 一、什么是一维数组 一维数组是一种常见的数据结构,它由相同类型的数据元素按顺序排列,并以一个变量名引用整个数组,在C语言中,数组的下标从0开始,下标的最大值为数组长度减1。 二、C语言使用指针的一维数组 在C语言中,我们可以使用指针来访问一维数组中的元素,常用的访问方式有两种:指针加下标和指针变量。 2.1 …

    C 2023年5月9日
    00
  • 使用C语言实现学生成绩管理系统

    使用C语言实现学生成绩管理系统是一项常见的编程任务,本攻略详细讲解了如何使用C语言实现学生成绩管理系统,内容包括: 需求分析 设计系统架构 设计数据结构 编写程序代码 进行测试 下面详细讲解每一步。 需求分析:首先需要明确学生成绩管理系统的功能,常见的功能有:添加学生信息、修改学生信息、删除学生信息、查询学生信息和统计学生成绩等。 设计系统架构:设计学生成绩…

    C 2023年5月23日
    00
  • CCleaner如何修复注册表 CCleaner修复注册表教程

    CCleaner如何修复注册表 CCleaner是一款功能丰富、广受用户欢迎的免费系统清理和优化工具,其中修复注册表功能可以清理无用的注册表项,帮助优化电脑性能。下面介绍CCleaner如何修复注册表。 步骤1:打开CCleaner 首先,下载并安装CCleaner软件,并打开该软件。 步骤2:选择注册表 点击左侧的“注册表”选项卡。(注:在使用注册表工具时…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部