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日

相关文章

  • MySQL中的JSON字段List成员检查

    标题:MySQL中的JSON字段List成员检查 1. JSON字段List成员检查 在MySQL中,可以使用JSON字段来存储数据。在JSON字段中,可以包含List类型数据。如果需要检查一个List数据中是否包含某个成员,可以使用MySQL中提供的JSON函数进行查询。 2. 使用JSON_CONTAINS函数 使用JSON_CONTAINS函数可以判断…

    other 2023年6月25日
    00
  • R语言-实现list的嵌套与提取嵌套中的值

    R语言-实现list的嵌套与提取嵌套中的值 在R语言中,可以使用list数据结构来创建嵌套的列表,并且可以通过索引和递归的方式提取嵌套列表中的值。下面是一个完整的攻略,包含了创建嵌套列表和提取嵌套值的过程。 创建嵌套列表 要创建一个嵌套列表,可以使用list()函数,并在其中嵌套其他的列表或向量。下面是一个示例: # 创建一个嵌套列表 nested_list…

    other 2023年7月28日
    00
  • linux一些基本命令以及初级网络配置方法

    Linux基本命令 目录和文件命令 cd:进入到指定目录,用法:cd 目录路径 ls:列出当前目录下的所有文件和目录,用法:ls mkdir:创建一个新目录,用法:mkdir 目录名 touch:创建一个新文件,用法:touch 文件名 rm:删除一个文件或目录,用法:rm 文件名 或 rm -r 目录 文件编辑命令 vi:用于编辑文本文件,常用的命令有: …

    other 2023年6月26日
    00
  • vue 路由嵌套高亮问题的解决方法

    Vue 路由嵌套高亮问题的解决方法攻略 在 Vue 中,当使用路由嵌套时,我们可能会遇到一个常见的问题:如何在嵌套路由中正确地高亮当前活动的链接。本攻略将详细介绍解决这个问题的方法,并提供两个示例说明。 方法一:使用动态类绑定 Vue 提供了一种简单的方法来解决路由嵌套高亮问题,即使用动态类绑定。我们可以通过在路由链接上绑定一个动态类,根据当前路由的路径来判…

    other 2023年7月28日
    00
  • 在scrollView中使用pageControl

    在scrollView中使用pageControl的完整攻略 在iOS开发中,scrollView是一个常用的控件,用于显示大量内容。而pageControl则是一个用于指示scrollView当前页数的控件。本文将为您提供一份详细的在scrollView中使用pageControl的完整攻略,包括基本概念、使用方法和两个示例说明。 基本概念 在iOS中,s…

    other 2023年5月5日
    00
  • 一步一步学android控件(之十六)——checkbox

    一步一步学Android控件(之十六)——CheckBox CheckBox是一个常见的Android控件,可以用于实现单选和多选。在本文中,我们将分步骤介绍如何使用CheckBox控件。 创建CheckBox 要创建一个CheckBox,可以在XML布局文件中使用<CheckBox>元素: <CheckBox android:id=&qu…

    其他 2023年3月28日
    00
  • android延时执行的几种方式

    Android延时执行的几种方式 在Android开发中,经常需要使用延时执行的操作。例如,需要在某个时间后自动执行某个任务,或者需要在UI线程忙碌时,将某个操作延后执行,以避免阻塞UI线程。本文将介绍Android中延时执行的几种方式。 1. 使用Handler.postDelayed()方法 Handler是Android中的一个消息处理机制,它与线程之…

    其他 2023年3月28日
    00
  • redis启动流程介绍

    Redis启动流程介绍 在介绍Redis启动流程之前,我们先了解一下Redis的架构:1. Redis是一个单线程的数据库,所有的数据都存储在内存中,以保证读写性能。2. Redis使用事件驱动的模型,它使用IO多路复用机制,同时处理多个客户端请求。 Redis启动流程详解 Redis的启动流程主要可以分为以下几个步骤: 1. 加载配置文件 首先,Redis…

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