c++详细讲解构造函数的拷贝流程

c++详细讲解构造函数的拷贝流程

什么是构造函数

在C++中,构造函数是一种特殊的成员函数,用于创建和初始化对象。当一个对象被创建时,构造函数会自动调用,完成对象的初始化工作。

构造函数的拷贝流程

当需要创建一个新对象并将其初始化为另一个对象的副本时,就需要使用到拷贝构造函数。拷贝构造函数用于实现一个对象复制另一个对象的所有成员变量的功能。

在C++中,每个类都有一个默认的拷贝构造函数,它会按照成员变量的顺序逐个复制。以下是一个简单的示例:

class Person {
public:
    int age;
    std::string name;
};

int main() {
    Person p1;
    p1.age = 20;
    p1.name = "Tom";

    Person p2 = p1; // 使用拷贝构造函数将p1复制给p2

    std::cout << "p1's age is " << p1.age << ", name is " << p1.name << std::endl;
    std::cout << "p2's age is " << p2.age << ", name is " << p2.name << std::endl;

    return 0;
}

输出结果为:

p1's age is 20, name is Tom
p2's age is 20, name is Tom

可以看到,p1和p2的成员变量都被正确地复制过来了。拷贝构造函数会在创建新对象时调用,并将原对象的值拷贝到新对象上。

浅拷贝

以上示例中的拷贝构造函数实现的是浅拷贝。浅拷贝只是将原对象的成员变量的值复制过来,并没有创建新的对象,也没有为成员变量分配新的内存空间。这种方式的缺点是,如果原对象和副本对象指向同一块内存时,会导致副本对象发生意外修改,原对象的值也会被改变。

以下是一个示例:

class Person {
public:
    int age;
    std::string* name;
    Person(int age, std::string* name) {
        this->age = age;
        this->name = name;
    }
};

int main() {
    std::string name = "Tom";
    Person p1(20, &name);
    Person p2 = p1; // 使用拷贝构造函数将p1复制给p2

    std::cout << "p1's name is " << *(p1.name) << std::endl;
    std::cout << "p2's name is " << *(p2.name) << std::endl;

    *(p2.name) = "Jerry"; // 修改p2的name

    std::cout << "After modification:" << std::endl;
    std::cout << "p1's name is " << *(p1.name) << std::endl;
    std::cout << "p2's name is " << *(p2.name) << std::endl;

    return 0;
}

输出结果为:

p1's name is Tom
p2's name is Tom
After modification:
p1's name is Jerry
p2's name is Jerry

可以看到,修改了p2的成员变量name,同时也修改了p1的成员变量name。这是因为p1和p2的成员变量name指向同一块内存空间,造成了意外修改。

深拷贝

为了规避上述的问题,需要使用到深拷贝。深拷贝会为新对象重新分配内存空间,并将原对象的值复制到新的内存空间中,从而避免了意外修改。

以下是一个示例:

class Person {
public:
    int age;
    std::string* name;
    Person(int age, std::string* name) {
        this->age = age;
        this->name = new std::string(*name); // 新分配内存空间,拷贝name指向的字符串
    }
    ~Person() {
        delete name; // 在析构函数中释放内存空间
    }
    Person(const Person& p) { // 拷贝构造函数
        this->age = p.age;
        this->name = new std::string(*(p.name)); // 深拷贝
    }
};

int main() {
    std::string name = "Tom";
    Person p1(20, &name);
    Person p2 = p1; // 使用拷贝构造函数将p1复制给p2

    std::cout << "p1's name is " << *(p1.name) << std::endl;
    std::cout << "p2's name is " << *(p2.name) << std::endl;

    *(p2.name) = "Jerry"; // 修改p2的name

    std::cout << "After modification:" << std::endl;
    std::cout << "p1's name is " << *(p1.name) << std::endl;
    std::cout << "p2's name is " << *(p2.name) << std::endl;

    return 0;
}

输出结果为:

p1's name is Tom
p2's name is Tom
After modification:
p1's name is Tom
p2's name is Jerry

可以看到,修改了p2的成员变量name后,p1的成员变量name并没有发生改变。这是因为p1和p2的成员变量name指向不同的内存空间,使用深拷贝避免了意外修改。同时,在析构函数中释放了拷贝出来的内存空间。

结论

构造函数、拷贝构造函数是自定义类中必须要定义的两个函数。拷贝构造函数会在创建新对象时调用,并将原对象的值拷贝到新对象上,在使用时需要注意浅拷贝和深拷贝的区别。如果对象中存在指针类型成员变量,则必须使用深拷贝避免数据异常。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:c++详细讲解构造函数的拷贝流程 - Python技术站

(0)
上一篇 2023年6月26日
下一篇 2023年6月26日

相关文章

  • 详解JavaScript中new操作符的解析和实现

    详解JavaScript中new操作符的解析和实现 什么是new操作符 new操作符用于创建一个新对象,它首先创建一个空对象,然后使用指定的构造函数来初始化这个对象,并返回它。例如: function Person(name, age) { this.name = name; this.age = age; } let person = new Person…

    other 2023年6月26日
    00
  • github如何添加ssh

    如何在GitHub中添加SSH Key 在GitHub中添加SSH Key可以让你在使用Git的过程中更加便捷,不需要每次都输入密码。本文将向你展示如何在GitHub上添加SSH Key。 步骤一:生成SSH Key 首先,你需要在本地生成SSH Key。打开终端(或命令行),输入以下命令: ssh-keygen -t rsa -b 4096 -C &quo…

    其他 2023年3月28日
    00
  • VFP与其他应用程序的集成

    为了将VFP与其他应用程序进行集成,可以使用多种方法和技术。下面详细讲解一下几种集成方法和技术。 使用OLE和COM对象 OLE和COM对象是一种常用的集成方法,可将VFP应用程序连接到其他应用程序。下面是一个示例,将VFP与Word应用程序进行集成。 步骤一:创建Word文档 在Word中创建一个新文档,然后保存在本地计算机上。文档路径可以在后面的代码中使…

    other 2023年6月25日
    00
  • 魔兽世界6.0猎人输出循环 生存射击兽王分析

    魔兽世界6.0猎人输出循环 生存射击兽王分析 生存猎人输出循环 生存猎人是一种以生存为主题的猎人职业,主要特点是德鲁伊的化身,能够使用治疗、控制和伤害技能等多种技能,能单独进行大部分任务。 生存猎人的输出循环主要包括以下几个步骤: 稳固射击:可以用于快速输出、击退一些小怪。每次施放该技能后,下次稳固射击的攻击速度将会提高。建议在怪物从远处奔向自己时就施放该技…

    other 2023年6月27日
    00
  • 使用MockMvc进行controller层单元测试 事务自动回滚的完整案例

    以下是关于使用MockMvc进行controller层单元测试的完整攻略,包含两个示例说明: 1. 添加依赖 首先,您需要在项目的pom.xml文件中添加MockMvc和JUnit的依赖。示例: <dependencies> <!– 添加MockMvc依赖 –> <dependency> <groupId>…

    other 2023年10月19日
    00
  • 手把手教你如何用fiddler抓取手机数据包(ios+android)

    手把手教你如何用Fiddler抓取手机数据包(iOS+Android) 如果你是一名开发者或者测试人员,在某些场景下,需要通过抓包来获取手机端和服务器之间的通信数据,以进行测试和分析。而目前市面上最为流行和实用的抓包工具,非Fiddler莫属。 本教程将以iOS和Android设备为例,演示使用Fiddler进行手机数据包抓取的具体方法。 一、安装Fiddl…

    其他 2023年3月29日
    00
  • MySQL数据库输入密码后闪退问题的解决方法

    下面就是详细讲解MySQL数据库输入密码后闪退的解决方法完整攻略: 问题背景 MySQL是一种开源数据库,常用于Web应用程序的后台支持。在使用MySQL时,经常会遇到以下问题:输入密码后闪退。 解决方法 MySQL输入密码后闪退问题通常是由于MySQL配置文件中的一些错误或问题导致的。可以通过以下步骤解决这个问题: 步骤1:检查MySQL配置文件 首先,打…

    other 2023年6月26日
    00
  • 基于java构造方法Vector创建对象源码分析

    基于Java构造方法Vector创建对象源码分析 介绍 在Java中,Vector是一个动态数组,它可以根据需要自动增长和缩小。Vector类提供了多个构造方法来创建Vector对象。本攻略将详细讲解如何使用构造方法创建Vector对象,并分析其源码。 构造方法 Vector类提供了以下几个常用的构造方法: Vector(): 创建一个空的Vector对象。…

    other 2023年8月6日
    00
合作推广
合作推广
分享本页
返回顶部