C++中的拷贝构造详解

C++中的拷贝构造详解

什么是拷贝构造函数

拷贝构造函数是C++类中的一种构造函数,用于创建对象的副本。当原对象被传递给一个函数或以值的方式返回时,拷贝构造函数被调用来创建一个新的对象,该新对象是原对象的一个完全拷贝。拷贝构造函数的原型通常是:ClassName(const ClassName& obj);

在某些情况下,编译器会自动生成拷贝构造函数。但在定义类时,我们也可以写自己的拷贝构造函数来控制如何创建新对象的副本。

拷贝构造函数的语法

拷贝构造函数的声明和定义都有一些要求:

ClassName(const ClassName& obj) {
    // 拷贝构造函数的实现
}
  1. 拷贝构造函数应采用传参方式而不是返回类型方式。
  2. 参数应为常量引用。在C++中,引用通常是比复制对象更好的方式,而常量引用可以避免误修改原始对象的风险。
  3. 拷贝构造函数的实现方法通常是将源对象的数据成员一一复制到新的对象中。

拷贝构造函数的使用

下面是一个简单类的定义,其中包含自己的拷贝构造函数:

class Person {
  public:
    int age;
    double height;

    // 拷贝构造函数
    Person(const Person &p) {
        age = p.age;
        height = p.height;
    }

    // 构造函数
    Person(int a, double h) {
        age = a;
        height = h;
    }
};

现在我们可以创建一个对象并将其拷贝到新对象中,使用代码如下:

int main() {
    Person p1(20, 1.8);
    Person p2 = p1;
}

这里我们创建了一个p1对象,然后将该对象拷贝到p2中。这将调用拷贝构造函数,为p2创建一个新的、完全相同的Person对象。

拷贝构造函数的使用示例

以下示例展示了如何使用拷贝构造函数来创建对象的深拷贝和浅拷贝。

浅拷贝

在这个示例中,Person类中的str成员是一个指针,指向存储在堆内存中的字符串。由于默认的拷贝构造函数实现只会复制指针本身,因此在执行浅拷贝时,它们所指的内存地址是相同的,这可能导致内存泄漏和其他问题。

#include <iostream>
#include <cstring>

class Person {
public:
    char* str;
    int age;

    Person():str(nullptr), age(0) {}
    Person(const char* s, int a):str(nullptr), age(a) {
        if (s) {
            str = new char[strlen(s) + 1];
            strcpy(str, s);
        }
    }

    ~Person() {
        delete [] str;
    }


    void print() {
        std::cout<<"age: "<< age<< " str: "<< str<<std::endl;
    }
};

int main() {
    Person p1("Mike", 18);
    Person p2 = p1; //浅拷贝
    p1.print();
    p2.print();

    return 0;
}

输出结果:

age: 18 str: Mike
age: 18 str: Mike

注意到,在浅拷贝示例中,两个Person对象的str成员的地址是相同的,这可能导致内存泄漏和其他问题。

深拷贝

为了解决上述问题,需要自定义拷贝构造函数,实现成员变量的深拷贝。

#include <iostream>
#include <cstring>

class Person {
public:
    char* str;
    int age;

    Person():str(nullptr), age(0) {}
    Person(const char* s, int a):str(nullptr), age(a) {
        if (s) {
            str = new char[strlen(s) + 1];
            strcpy(str, s);
        }
    }

    // 拷贝构造函数: 深拷贝成员变量
    Person(const Person& other):str(nullptr), age(other.age) {
        if (other.str) {
            str = new char[strlen(other.str) + 1];
            strcpy(str, other.str);
        }
    }

    ~Person() {
        delete [] str;
    }

    void print() {
        std::cout<<"age: "<< age<< " str: "<< str<<std::endl;
    }
};

int main() {
    Person p1("Mike", 18);
    Person p2 = p1; // 深拷贝
    p1.print();
    p2.print();

    return 0;
}

输出结果:

age: 18 str: Mike
age: 18 str: Mike

注意到,在深拷贝示例中,两个Person对象的str成员的地址是不同的,它们指向不同的内存地址,也不存在内存泄漏和其他问题。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++中的拷贝构造详解 - Python技术站

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

相关文章

  • 金士顿DT70怎么样 金士顿DT70USB-C闪存盘评测

    金士顿DT70USB-C闪存盘评测攻略 一、产品介绍 金士顿DT70USB-C闪存盘是一款最新推出的USB-C闪存盘,容量覆盖32GB、64GB和128GB三种。具有高速读写、持久耐用和多平台通用等特点,可广泛应用于PC、手机、平板、电视等设备中。 二、技术规格 规格 描述 存储容量 32GB/64GB/128GB 接口类型 USB-C 读取速度 最高可达2…

    C 2023年5月23日
    00
  • C语言实现电话订餐管理系统

    C语言实现电话订餐管理系统攻略 问题描述 设计并实现一个电话订餐管理系统。系统可以接受客户的电话预约,然后根据客户的要求将订单信息存储在数据库中。当客户来取餐或者订单需要被派送的时候,系统会根据订单ID获取订单信息并处理。 解决方案 使用C语言编写一个管理系统,包含以下功能: 添加订单信息 查找订单信息 修改订单信息 删除订单信息 数据结构设计 在编写管理系…

    C 2023年5月23日
    00
  • 安全账户管理器初始化失败 lsass.exe 0XC0000(SAM文件问题)

    安全账户管理器(LSASS,Local Security Authority Subsystem Service)是Windows操作系统中非常重要的一个组件,负责用户身份鉴定、安全策略实施等工作。如果在启动或者使用Windows操作系统时,出现了“安全账户管理器初始化失败 lsass.exe 0XC0000(SAM文件问题)”的错误提示,这通常是由于系统文…

    C 2023年5月23日
    00
  • C++的虚析构详解及实例代码

    C++的虚析构详解及实例代码 什么是虚析构函数 在 C++ 中,如果一个类中含有虚函数,我们通常都会将这个类的析构函数定义为虚析构函数,以保证对象的正确释放。 虚析构函数是在基类中定义,被子类继承并覆盖的析构函数。具有虚析构函数的类被用做其他类的基类,以确保正确地释放对象的所有资源。 虚析构函数的应用场景 假设我们有一个基类Base,它含有虚析构函数,另外还…

    C 2023年5月24日
    00
  • C语言中如何进行文件操作?

    当我们需要在C语言程序中读取或写入文件时,我们需要使用文件操作。在C语言中,文件操作可以通过C标准库中的文件处理函数来实现。下面是文件操作的完整攻略: 打开文件 我们首先需要使用fopen()函数打开一个文件。这个函数的语法为: FILE *fopen(const char *filename, const char *mode); 其中,filename参…

    C 2023年4月27日
    00
  • 利用Mongoose让JSON数据直接插入或更新到MongoDB

    下面我就详细讲解利用Mongoose让JSON数据直接插入或更新到MongoDB的攻略。 1. 环境准备 在开始操作之前,我们需要先安装MongoDB和Mongoose,并确保本地MongoDB服务已经启动。 安装Mongoose可以直接使用npm命令: npm install mongoose –save 2. 连接MongoDB数据库 在使用Mongo…

    C 2023年5月23日
    00
  • C语言中如何进行泛型编程?

    在C语言中进行泛型编程有多种方式,其中比较常用的方法是通过宏定义和结构体实现,下面分别介绍这两种方法的具体实现。 通过宏定义实现泛型编程 在C语言中,可以使用宏定义实现泛型函数的定义和调用。具体实现方式如下: 定义泛型函数的宏定义,例如下面定义了一个泛型的swap函数宏: #define SWAP(type, a, b) { type temp = a; a…

    C 2023年4月27日
    00
  • Java EE项目中的异常处理总结(一篇不得不看的文章)

    以下是我对《Java EE项目中的异常处理总结(一篇不得不看的文章)》这篇文章的完整攻略: 文章概述 文章主要分为四个部分:异常处理的基本概念、Java中的异常处理机制、Java EE项目中的异常处理、异常处理的最佳实践等。其中,第一个部分主要介绍了异常处理的基本概念,包括异常的定义、分类、抛出和捕获等。第二个部分则详细讲解了Java中的异常处理机制,包括t…

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