【解码现代 C++】:实现自己的智能 【String 类】

目录

1. 经典的String类问题

1.1 构造函数

小李的理解

1.2 析构函数

小李的理解

1.3 测试函数

小李的理解

1.4 需要记住的知识点

2. 浅拷贝

2.1 什么是浅拷贝

小李的理解

2.2 需要记住的知识点

3. 深拷贝

3.1 传统版写法的String类

3.1.1 拷贝构造函数

小李的理解

3.1.2 赋值运算符重载

小李的理解

3.1.3 需要记住的知识点

4. 现代版写法的String类

4.1 拷贝构造函数

小李的理解

4.2 赋值运算符重载

小李的理解

4.3 需要记住的知识点

5. 写时拷贝(了解)

5.1 写时拷贝

小李的理解

5.2 需要记住的知识点

​编辑

6. 总结

小李的理解


 

专栏:C++学习笔记 

接上一篇:【掌握C++ string 类】——【高效字符串操作】的【现代编程艺术】

在C++中,std::string是一个非常常用的类,它封装了对C风格字符串的处理。但是,在某些情况下,我们可能需要自己实现一个类似string的类来展示对C++核心概念的掌握。本文将深入剖析一个自定义的String类的实现,特别关注其构造、拷贝构造、赋值运算符重载以及析构函数的实现。

1. 经典的String类问题

以下是一个初步的String类实现:

#include <iostream>
#include <cstring>
#include <cassert>

class String {
public:
    // 构造函数,默认参数为空字符串
    String(const char* str = "") {
        if (nullptr == str) {
            assert(false);  // 断言检查
            return;
        }
        _str = new char[strlen(str) + 1];  // 分配内存
        strcpy(_str, str);  // 拷贝字符串
    }

    // 析构函数
    ~String() {
        if (_str) {
            delete[] _str;  // 释放内存
            _str = nullptr;  // 避免悬挂指针
        }
    }

private:
    char* _str;
};

// 测试函数
void TestString() {
    String s1("hello bit!!!");
    String s2(s1);  // 默认拷贝构造
}

int main() {
    TestString();
    return 0;
}

1.1 构造函数

  • 功能:用于初始化对象。
  • 操作
    • 检查输入指针是否为nullptr
    • 分配足够的内存存储字符串。
    • 拷贝字符串内容到新分配的内存中。
小李的理解
  • 构造函数:就像你去商店买东西,店员先检查你要买的东西是否存在,然后给你打包好交给你。

1.2 析构函数

  • 功能:用于释放对象占用的资源。
  • 操作
    • 检查指针是否为空。
    • 释放内存。
    • 将指针置为nullptr以避免悬挂指针。
小李的理解
  • 析构函数:当你不需要某样东西时,店员会帮你处理掉它,并确保不会再使用它。

1.3 测试函数

  • 功能:验证构造函数和析构函数的工作情况。
  • 操作
    • 创建两个String对象。
    • 用默认的拷贝构造函数创建第二个对象。
小李的理解
  • 测试函数:就像试用新买的东西,确保它们都能正常工作。

1.4 需要记住的知识点

  • 默认的拷贝构造函数执行的是浅拷贝(shallow copy)。
  • 多个实例共享同一块内存,当其中一个实例被销毁时,其他实例会尝试访问已经释放的内存,导致程序崩溃。

2. 浅拷贝

浅拷贝是指编译器仅仅复制对象中的值(即指针地址),而不是指针所指向的内容。这意味着多个对象会共享同一份资源,如上例中的字符数组:

String s1("hello bit!!!");
String s2(s1); // 浅拷贝,s1和s2共享同一块内存

2.1 什么是浅拷贝

  • 定义:浅拷贝只复制指针地址,多个对象共享同一份资源。
  • 问题:当一个对象释放内存时,其他对象会访问无效内存,导致程序崩溃。
小李的理解
  • 浅拷贝:就像两个孩子共用一个玩具,只有一个玩具,两人要共享。一个孩子用坏了,另一个孩子也不能用了。

2.2 需要记住的知识点

  • 浅拷贝会导致多个对象共享同一块内存。
  • 释放其中一个对象时,其他对象会尝试访问已释放的内存,导致程序崩溃。

3. 深拷贝

深拷贝则会创建对象时复制资源的内容,使每个对象拥有一份独立的资源。这需要显式定义拷贝构造函数和赋值运算符。以下是一个实现深拷贝的String类:

3.1 传统版写法的String

#include <iostream>
#include <cstring>
#include <cassert>

class String {
public:
    // 构造函数
    String(const char* str = "") {
        if (nullptr == str) {
            assert(false);  // 断言检查
            return;
        }
        _str = new char[strlen(str) + 1];  // 分配内存
        strcpy(_str, str);  // 拷贝字符串
    }

    // 拷贝构造函数
    String(const String& s) {
        _str = new char[strlen(s._str) + 1];  // 分配内存
        strcpy(_str, s._str);  // 拷贝字符串
    }

    // 赋值运算符重载
    String& operator=(const String& s) {
        if (this != &s) {  // 自我赋值检查
            char* pStr = new char[strlen(s._str) + 1];  // 分配新内存
            strcpy(pStr, s._str);  // 拷贝字符串
            delete[] _str;  // 释放旧内存
            _str = pStr;  // 更新指针
        }
        return *this;
    }

    // 析构函数
    ~String() {
        if (_str) {
            delete[] _str;  // 释放内存
            _str = nullptr;  // 避免悬挂指针
        }
    }

private:
    char* _str;
};

// 测试函数
void TestString() {
    String s1("hello bit!!!");
    String s2(s1);  // 使用拷贝构造函数
    String s3 = s2;  // 使用赋值运算符重载
}

int main() {
    TestString();
    return 0;
}

3.1.1 拷贝构造函数

  • 功能:创建新对象时分配新的内存,并拷贝字符串内容。
  • 操作
    • 分配足够的内存存储字符串。
    • 拷贝字符串内容到新分配的内存中。
小李的理解
  • 拷贝构造函数:就像父母给每个孩子都买一份玩具,各自玩各自的,不会有冲突。

3.1.2 赋值运算符重载

  • 功能:确保自我赋值时不会出错,并实现深拷贝。
  • 操作
    • 检查自我赋值(如s = s)。
    • 分配新内存,拷贝字符串,释放旧内存,并更新指针。
小李的理解
  • 赋值运算符重载:就像你决定换掉旧的玩具,先买个新的,再把旧的处理掉,确保整个过程不会出错。

3.1.3 需要记住的知识点

  • 拷贝构造函数和赋值运算符必须显式定义以实现深拷贝。
  • 深拷贝确保每个对象都有独立的资源,避免共享同一块内存。

4. 现代版写法的String

现代C++中,可以利用临时对象和swap函数简化赋值运算符的实现:

#include <iostream>
#include <cstring>
#include <cassert>
#include <algorithm>  // 包含swap函数

class String {
public:
    // 构造函数
    String(const char* str = "") {
        if (nullptr == str) {
            assert(false);  // 断言检查
            return;
        }
        _str = new char[strlen(str) + 1];  // 分配内存
        strcpy(_str, str);  // 拷贝字符串
    }

    // 拷贝构造函数
    String(const String& s)
        : _str(nullptr) {
        String strTmp(s._str);  // 创建临时对象
        swap(_str, strTmp._str);  // 交换内容
    }

    // 赋值运算符重载
    String& operator=(String s) {
        swap(_str, s._str);  // 交换内容
        return *this;
    }

    // 析构函数
    ~String() {
        if (_str) {
            delete[] _str;  // 释放内存
            _str = nullptr;  // 避免悬挂指针
        }
    }

private:
    char* _str;
};

// 测试函数
void TestString() {
    String s1("hello bit!!!");
    String s2(s1);  // 使用拷贝构造函数
    String s3 = s2;  // 使用赋值运算符重载
}

int main() {
    TestString();
    return 0;
}

4.1 拷贝构造函数

  • 功能:利用临时对象实现深拷贝。
  • 操作
    • 创建一个临时对象。
    • 交换临时对象和当前对象的内容。
小李的理解
  • 拷贝构造函数:就像把新玩具给孩子,然后把旧玩具处理掉,确保整个过程不会出错。

4.2 赋值运算符重载

  • 功能:利用临时对象和swap函数简化赋值运算符的实现。
  • 操作
    • 利用临时对象进行深拷贝。
    • 交换临时对象和当前对象的内容。
小李的理解
  • 赋值运算符重载:就像在家里换家具时,先把新家具搬进来,再把旧家具搬走,确保整个过程不会出错。

4.3 需要记住的知识点

  • 现代C++中,利用swap和临时对象简化赋值运算符的实现,可以确保异常安全。
  • 这种方法使得代码简洁且高效。

5. 写时拷贝(了解)

写时拷贝(Copy-On-Write, COW)是一种优化技术,在实现浅拷贝的基础上增加引用计数。每次拷贝时增加引用计数,只有在实际写操作发生时才进行深拷贝:

#include <iostream>
#include <cstring>
#include <cassert>

class String {
public:
    // 构造函数
    String(const char* str = "") {
        _str = new char[strlen(str) + 1];
        strcpy(_str, str);
        _refCount = new int(1);  // 引用计数
    }

    // 拷贝构造函数
    String(const String& s)
        : _str(s._str), _refCount(s._refCount) {
        ++(*_refCount);  // 增加引用计数
    }

    // 赋值运算符重载
    String& operator=(const String& s) {
        if (this != &s) {
            if (--(*_refCount) == 0) {  // 释放旧资源
                delete[] _str;
                delete _refCount;
            }
            _str = s._str;
            _refCount = s._refCount;
            ++(*_refCount);  // 增加引用计数
        }
        return *this;
    }

    // 析构函数
    ~String() {
        if (--(*_refCount) == 0) {  // 释放资源
            delete[] _str;
            delete _refCount;
        }
    }

private:
    char* _str;
    int* _refCount;  // 引用计数指针
};

// 测试函数
void TestString() {
    String s1("hello bit!!!");
    String s2(s1);  // 增加引用计数
    String s3 = s2;  // 增加引用计数
}

int main() {
    TestString();
    return 0;
}

5.1 写时拷贝

  • 定义:通过引用计数实现资源共享,仅在写操作时进行深拷贝。
  • 操作
    • 每次拷贝时增加引用计数。
    • 只有在实际写操作发生时才进行深拷贝。
小李的理解
  • 写时拷贝:就像兄弟姐妹共享一个玩具,只有在其中一个想要修改玩具时,才会给他一个新的玩具。

5.2 需要记住的知识点

  • 写时拷贝通过引用计数优化资源管理,减少不必要的深拷贝操作。
  • 这种技术在某些情况下能提高性能,但也有复杂性增加和多线程不安全的问题。

6. 总结

实现一个自定义的String类,最重要的是理解和正确实现构造函数、拷贝构造函数、赋值运算符重载和析构函数。通过深拷贝和写时拷贝等技术,可以确保对象管理资源的正确性和高效性。

小李的理解
  • 构造函数:初始化对象,确保资源正确分配。
  • 析构函数:释放资源,避免内存泄漏。
  • 拷贝构造函数:深拷贝确保每个对象有独立资源。
  • 赋值运算符重载:自我赋值检查,深拷贝确保安全赋值。
  • 现代C++:利用swap和临时对象简化代码,实现异常安全。
  • 写时拷贝:优化资源管理,通过引用计数延迟深拷贝操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/774095.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Pspice添加新的元器件

1.下载好的Pspice的模型文件。 2.将模型文件的&#xff0c;识别类型修改为 lib 选择Pspice的模型路径 会立马跳出&#xff0c;下面的这个窗口。 核实元器件图形&#xff0c;没问题。 添加Pspic仿真模型文件 验证&#xff0c;是否添加模型文件成功 使用模型文件

学会python——用python制作一个登录和注册窗口(python实例十八)

目录 1.认识Python 2.环境与工具 2.1 python环境 2.2 Visual Studio Code编译 3.登录和注册窗口 3.1 代码构思 3.2 代码实例 3.3 运行结果 4.总结 1.认识Python Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的可读…

扫地机器人如何利用图算法来进行避障策略和优化清扫路径的?

前言 扫地机器人是现代家庭中最常见的智能设备。其基本的核心组件由主控系统&#xff08;大脑&#xff09;、传感器等控制系统&#xff08;感知系统&#xff09;、动力供应系统&#xff08;心脏&#xff09;、清扫系统&#xff08;四肢&#xff09;组成。 扫地机器人的智能、高…

【中项第三版】系统集成项目管理工程师 | 第 9 章 项目管理概论② | 9.4 - 9.5

前言 第 9 章对应的内容选择题和案例分析都会进行考查&#xff0c;这一章节理论性较强&#xff0c;学习要以教材为准。本章分值预计在4-5分。 目录 9.4 项目生命周期和项目阶段 9.4.1 定义与特征 9.4.2 生命周期类型 9.5 项目立项管理 9.5.1 项目建议与立项申请 9.5.2 …

交换数字00

题目链接 交换数字 题目描述 注意点 numbers.length 2-2147483647 < numbers[i] < 2147483647 解答思路 不适用临时变量&#xff0c;可以先将numbers[0]和numbers[1]的信息都存到某个位置&#xff08;可以相加可以相减或其他位操作&#xff09;&#xff0c;然后另一…

昇思MindSpore学习笔记4-02生成式--DCGAN生成漫画头像

摘要&#xff1a; 记录了昇思MindSpore AI框架使用70171张动漫头像图片训练一个DCGAN神经网络生成式对抗网络&#xff0c;并用来生成漫画头像的过程、步骤。包括环境准备、下载数据集、加载数据和预处理、构造网络、模型训练等。 一、概念 深度卷积对抗生成网络DCGAN Deep C…

MMSC物料库位扩充

MMSC物料库位扩充 输入事务码MMSC&#xff1a; 回车后添加新的库位即可&#xff1a; 代码实现&#xff0c;使用BDC *&------------------------------------------------* *&BDC的定义 *&------------------------------------------------* DATA gt_bdcdata T…

【UE5.1】Chaos物理系统基础——03 炸开几何体集

目录 步骤 一、通过径向向量将几何体集炸开 二、优化炸开效果——让破裂的碎块自然下落 三、优化炸开效果——让碎块旋转起来 四、优化炸开效果——让碎块旋转的越来越慢 步骤 一、通过径向向量将几何体集炸开 1. 打开上一篇中&#xff08;【UE5.1】Chaos物理系统基础—…

百度出品_文心快码Comate提升程序员效率

1.文心快码 文心快码包含指令、插件 和 知识三种功能&#xff0c; 1&#xff09;指令包含Base64编码、Base64解码、JSON转TS类型、JSON转YAML、JWT解码喂JSON。 2&#xff09;插件包含 3&#xff09;指令包含如下功能&#xff1a; 官网链接

Jenkins 强制杀job

有时候有的jenkins job运行时间太长&#xff0c;在jenkins界面点击x按钮进行abort&#xff0c;会失败&#xff1a; 这时候点击&#xff1a; “Click here to forcibly terminate running steps” 会进一步kill 任务&#xff0c;但是也还是有杀不掉的可能性。 终极武器是jenkin…

Aigtek电压放大器参数有哪些

电压放大器是广泛应用于电子电路中的一种重要电路元件&#xff0c;它主要用于将输入信号的电压放大到所需的输出电压水平。在设计和使用电压放大器时&#xff0c;我们需要了解并考虑一系列的参数和特性。本文将详细介绍电压放大器的主要参数&#xff0c;包括放大倍数、带宽、输…

springboot校园购物网站APP-计算机毕业设计源码041037

摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&#xff0c;使信息存…

【国产开源可视化引擎Meta2d.js】图层

独立图层 每个图元都有先后绘画顺序&#xff0c;即每个图元拥有一个独立图层&#xff0c;即meta2d.data().pens的数组索引。 可以通过meta2d.top/bottom/up/down等函数改变独立图层顺序。 分组图层 通过标签可以标识一个分组图层&#xff0c;通过meta2d.find(图层标签)获取…

己内酰胺纯化除杂的最佳工艺

己内酰胺纯化除杂的最佳工艺包括结晶法、离子交换树脂法、精馏法和萃取法等&#xff0c;每种方法都有其特定的应用场景和优缺点。以下是对这些方法的详细介绍&#xff1a; 最佳工艺介绍 ● 结晶法&#xff1a;通过调节pH值&#xff0c;使己内酰胺在特定条件下结晶&#xff0…

yolov8环境安装(可修改代码版本,源代码安装)

下载下来源文件以后&#xff0c;进去文件目录&#xff0c;然后输入pip指令&#xff0c;即可安装yolov8 cd ultralytics-main pip install -e . 直接使用pip安装的情况 当你使用pip install ultralytics这样的命令安装YOLOv8时&#xff0c;你实际上是在从Python包索引&#x…

C#实战|账号管理系统:通用登录窗体的实现。

哈喽,你好啊,我是雷工! 本节记录登录窗体的实现方法,比较有通用性,所有的项目登录窗体实现基本都是这个实现思路。 一通百通,以下为学习笔记。 01 登录窗体的逻辑 用户在登录窗输入账号和密码,如果输入账号和密码信息正确,点击【登录】按钮,则跳转显示主窗体,同时在固…

AI提示词:一个能让你的AI提升10倍逻辑能力的提示词,只有这几个字,Kimi和GPT都适用!

昨天晚上和朋友聊天&#xff0c;聊到AI提示词在实际使用过程中的逻辑能力问题。 他也是一个AI提示词的重度使用者&#xff0c;但是会经常遇到一个问题&#xff1a;明明觉得自己的提示词描述的很清楚了&#xff0c;可是AI输出的内容还是达不到自己想要的效果。 今天给大家分享…

认识不-物联网“六域模型”有哪些有什么作用

如下参考源于苏州稳联授权可见认知域-感知域-网络域-应用域-管理域-安全域-物联网六域模型 苏州稳联 (iotrouter.cn) 认识物联网“六域模型”&#xff1a;构成与作用 “六域模型”是一个有效的框架。这个模型通过将物联网划分为六个相互关联的域&#xff0c;帮助我们更好地理…

【网络安全】漏洞挖掘之Spring Cloud注入漏洞

漏洞描述 Spring框架为现代基于java的企业应用程序(在任何类型的部署平台上)提供了一个全面的编程和配置模型。 Spring Cloud 中的 serveless框架 Spring Cloud Function 中的 RoutingFunction 类的 apply 方法将请求头中的“spring.cloud.function.routing-expression”参数…

免费的二级域名分发,您确定不要试试吗?

这是可爱的小盆友做的一个免费的二级域名&#xff0c;目前上线就送114514个积分&#xff0c;名额有限~ 首先进入>路明二级域名分发 - 免费稳定的二级域名分发服务 (kmyl.top)< 进来是这个样子&#xff08;如下图所示&#xff09; 话不多说&#xff0c;进入教程~ 第一章…