C++之《剑指offer》学习记录(3):拷贝构造函数

笔者最近在找工作时,无意间读到了一本名为《剑指offer》的书,粗略翻阅了一下,感觉这将会是一本能让我不再苦恼于笔试和面试"手搓代码"的书。故笔者写下该系列博客记录自己的学习历程,希望能和这本书的读者朋友们一起交流学习心得。

介绍:《剑指Offer:名企面试官精讲典型编程题(第2版)》剖析了80个典型的编程面试题,系统整理基础知识、代码质量、解题思路、优化效率和综合能力这5个面试要点。

编程题链接:牛客网在线编程_算法面试_面试必刷TOP101 (nowcoder.com)

本博客关键词:拷贝构造函数深拷贝和浅拷贝

拷贝构造函数

构造函数的概念大家应该都不陌生,但是拷贝构造函数是什么呢?这里直接放一段代码:

cpp 复制代码
class MyClass
{
private:
    int age;
    char *name;

public:
    // 构造函数
    MyClass(const char *n, int s_age)
    {
        name = new char[strlen(n) + 1];
        strcpy(name, n);
        age = s_age;
        cout << "调用构造函数" << endl;
    }

    // 拷贝构造函数,也叫复制构造函数
    MyClass(const MyClass &other)
    {
        // 深拷贝
        // name = new char[strlen(other.name) + 1];
        // strcpy(name, other.name);
        age = other.age;
        // 浅拷贝
        name = other.name;
        cout << "调用复制构造函数" << endl;
    }
    ~MyClass()
    {
        cout << "析构函数被调用" << endl;
    }

    void getValue()
    {
        cout << "age: " << age << endl;
    }

    void setValue(int s_age)
    {
        age = s_age;
    }

    void getName()
    {
        cout << "name: " << name << endl;
    }

    void setName(const char *new_name)
    {
        strcpy(name, new_name);
    }
};

其中MyClass(const MyClass &other)就是拷贝构造函数,大家之后写拷贝构造函数的输入可以默认用const 类名 &other来写

  • const的使用,保证了拷贝构造函数不会修改传入的对象。这是符合逻辑的,因为拷贝构造的目的是创建一个新对象,而不是修改现有对象。
  • &,通过引用传参,这一步是必须的 ,如果使用按值传参,会导致永无休止的递归调用 。如果不使用引用传参,代码在编译阶段就会出错的。
    输入参数使用const修饰应该比较容易理解,可是为什么一定要通过引用传参?为什么按值传参会有无限的递归调用呢?下面简单做个解释:
  1. 拷贝构造函数一般会在以下几个场景被调用:
    • 用一个已存在的对象初始化另一个对象
    • 对象作为值传递给函数时
    • 在容器中存储对象时
    • 函数按值返回对象时(可能不会调用)
  2. 由以上可知,在对象作为值传递给函数时,也会调用拷贝构造函数,如果拷贝构造函数本身的输入参数也是按值传递的(输入参数为按值传递会进行值拷贝),则在形参拷贝实参的过程中又会产生拷贝构造函数,如此就会一直递归调用拷贝构造函数,最终导致栈溢出。
  3. 而通过引用传参,就避免了值拷贝的过程,传入拷贝构造函数的是另一个实例的引用。通过引用传递,可以减少类中构造函数和析构函数调用的次数,提高程序的执行效率

深拷贝和浅拷贝

  1. 深拷贝浅拷贝 是两种对象复制的方式,它们在复制对象时处理内存和资源的方式不同,尤其是在涉及动态分配内存或指针的情况下。
  2. 深拷贝和浅拷贝在基本类型变量(如int,double等)上的拷贝没有太多的差别,都是按值拷贝,会开辟新的空间。比如对象A的val值拷贝给对象B之后,B的val值会占用新的空间,修改B的val值不会影响A的val值。
  3. 对于指针类型变量,深浅拷贝区别就很大
    • 浅拷贝:在复制对象时,只复制对象的内存地址和基本属性(指针类型),而不复制它所指向的数据,意味着原对象和新对象将共享相同的内存区域或资源。
    • 深拷贝:在复制对象时,不仅复制对象的内存地址和基本属性(指针类型),还复制所有动态分配的内存或资源。也就是说,它会为新对象分配一个新的内存区域,并将原对象的数据复制到这个新区域中。
  4. 以拷贝构造函数为例:
cpp 复制代码
// 拷贝构造函数,也叫复制构造函数
    MyClass(const MyClass &other)
    {
        // 深拷贝
        name = new char[strlen(other.name) + 1];
        strcpy(name, other.name);
        // 浅拷贝
        // name = other.name;
        
        age = other.age;
        cout << "调用复制构造函数" << endl;
    }

测试用例

cpp 复制代码
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;

class MyClass
{
private:
    int age;
    char *name;

public:
    // 构造函数
    MyClass(const char *n, int s_age)
    {
        name = new char[strlen(n) + 1];
        strcpy(name, n);
        age = s_age;
        cout << "调用构造函数" << endl;
    }

    // 拷贝构造函数,也叫复制构造函数
    MyClass(const MyClass &other)
    {
        // 深拷贝
        // name = new char[strlen(other.name) + 1];
        // strcpy(name, other.name);
        age = other.age;
        // 浅拷贝
        name = other.name;
        cout << "调用复制构造函数" << endl;
    }
    ~MyClass()
    {
        cout << "析构函数被调用" << endl;
    }

    void getValue()
    {
        cout << "age: " << age << endl;
    }

    void setValue(int s_age)
    {
        age = s_age;
    }

    void getName()
    {
        cout << "name: " << name << endl;
    }

    void setName(const char *new_name)
    {
        strcpy(name, new_name);
    }
};

void show(MyClass &obj1, MyClass &obj2)
{
    obj1.getName();
    obj1.getValue();
    obj2.getName();
    obj2.getValue();
}

MyClass createClass()
{
    cout << "createClass:" << endl;
    MyClass obj("王五", 22);
    return obj; // 这里在GCC中并不会调用拷贝构造函数
}

int main()
{
    vector<MyClass> vec;

    MyClass obj1("张三", 23);
    MyClass obj2 = obj1; // 这里会调用拷贝构造函数------用一个已存在的对象初始化另一个对象

    cout << "vector:" << endl;
    vec.push_back(obj1); // 这里会调用拷贝构造函数------在容器中存储对象时

    MyClass obj = createClass(); 
    obj.getName();

    show(obj1, obj2);

    obj1.setName("李四");
    obj1.setValue(18);

    show(obj1, obj2);

    return 0;
}
相关推荐
I_Am_Me_9 分钟前
【JavaEE进阶】 JavaScript
开发语言·javascript·ecmascript
暮色_年华11 分钟前
Modern Effective C++item 9:优先考虑别名声明而非typedef
c++
重生之我是数学王子19 分钟前
QT基础 编码问题 定时器 事件 绘图事件 keyPressEvent QT5.12.3环境 C++实现
开发语言·c++·qt
Ai 编码助手21 分钟前
使用php和Xunsearch提升音乐网站的歌曲搜索效果
开发语言·php
学习前端的小z25 分钟前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
CV学术叫叫兽32 分钟前
一站式学习:害虫识别与分类图像分割
学习·分类·数据挖掘
神仙别闹33 分钟前
基于C#和Sql Server 2008实现的(WinForm)订单生成系统
开发语言·c#
XINGTECODE34 分钟前
海盗王集成网关和商城服务端功能golang版
开发语言·后端·golang
我们的五年43 分钟前
【Linux课程学习】:进程程序替换,execl,execv,execlp,execvp,execve,execle,execvpe函数
linux·c++·学习
zwjapple1 小时前
typescript里面正则的使用
开发语言·javascript·正则表达式