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

news2024/11/18 14:37:33

目录

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和临时对象简化代码,实现异常安全。
  • 写时拷贝:优化资源管理,通过引用计数延迟深拷贝操作。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

go zero入门

一、goctl安装 goctl 是 go-zero 的内置脚手架&#xff0c;可以一键生成代码、文档、部署 k8s yaml、dockerfile 等。 # Go 1.16 及以后版本 go install github.com/zeromicro/go-zero/tools/goctllatest检查是否安装成功 $ goctl -v goctl version 1.6.6 darwin/amd64vscod…

0/1背包问题总结

文章目录 &#x1f347;什么是0/1背包问题&#xff1f;&#x1f348;例题&#x1f349;1.分割等和子集&#x1f349;2.目标和&#x1f349;3.最后一块石头的重量Ⅱ &#x1f34a;总结 博客主页&#xff1a;lyyyyrics &#x1f347;什么是0/1背包问题&#xff1f; 0/1背包问题是…

html三级菜单

示例 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>Menu Example</title> <link re…

加速度传感器信号处理注意事项

1 传感器分类 对于压电式压力传感器而言&#xff0c;输出信号是最重要的选择标准之一。压电式压力传感器与电子电路相连&#xff0c;电子电路将传感器产生的电荷成比例转换为电压。 如果选用外部设备&#xff08;电荷放大器&#xff09;充当电子元件&#xff0c;则称其为电…

MYSQL篇二:数据库的操作

文章目录 1. 创建数据库1.1 查看数据库列表1.2 创建与删除数据库 2. 数据的编码问题3. 字符集和校验规则3.1 查看系统默认字符集以及校验规则3.2 查看数据库支持的字符集3.3 查看数据库支持的字符集校验规则3.4 校验规则对数据库的影响 4. 操纵数据库4.1 查看当前是哪一个数据库…

力扣热100 滑动窗口

这里写目录标题 3. 无重复字符的最长子串438. 找到字符串中所有字母异位词 3. 无重复字符的最长子串 左右指针left和right里面的字符串一直是没有重复的 class Solution:def lengthOfLongestSubstring(self, s: str) -> int:# 左右指针leftright0ans0#初始化结果tablecolle…

LMT加仿真,十一届大唐杯全国总决赛

这次省赛带了太多个省一了&#xff0c;并且很多都进入了国赛总决赛&#xff0c;具体可看下面的图片&#xff0c;只放了一部分。目前只有B组是只有一个商用设备赛也就是LMT&#xff0c;A组和高职组都是仿真实践赛加上商用设备赛。 针对商用设备赛有对应的资料&#xff…

基于IIS的Windows系统Django项目本地部署

参考&#xff1a; 1. 基于Windows平台的Django本地部署和腾讯云服务器上部署&#xff08;1&#xff09;_如何在服务器上发布部署django程序 csdn-CSDN博客 2.Windows server iis部署Django详细操作 - Django中文 - 博客园 (cnblogs.com) 3.在IIS中部署pythonDjango项目时出…

大模型范式下的知识检索增强实践(非常详细)零基础入门到精通,收藏这一篇就够了

导读 OpenKG新开设“TOC专家谈”栏目&#xff0c;推送OpenKG TOC&#xff08;技术监督委员会&#xff09;专家成员的观点文章。本期邀请到阿里巴巴通义实验室自然语言处理方向负责人黄非研究员介绍通义大模型在知识检索增强方面的一些实践。 随着人工智能技术的飞速发展&…

AI绘画 Stable Diffusion图像的脸部细节控制——采样器全解析

大家好&#xff0c;我是画画的小强 我们在运用AI绘画 Stable Diffusion 这一功能强大的AI绘图工具时&#xff0c;我们往往会发现自己对提示词的使用还不够充分。在这种情形下&#xff0c;我们应当如何调整自己的策略&#xff0c;以便更加精确、全面地塑造出理想的人物形象呢&a…

数据特征采样在 MySQL 同步一致性校验中的实践

作者&#xff1a;vivo 互联网存储研发团队 - Shang Yongxing 本文介绍了当前DTS应用中&#xff0c;MySQL数据同步使用到的数据一致性校验工具&#xff0c;并对它的实现思路进行分享。 一、背景 在 MySQL 的使用过程中&#xff0c;经常会因为如集群拆分、数据传输、数据聚合等…

24_嵌入式系统输入输出设备

目录 GPIO原理与结构 A/D接口基本原理 A/D接口原理 A/D转换的重要指标 D/A接口基本原理 D/A接口原理 DAC的分类 D/A转换器的主要指标 键盘接口基本原理 键盘接口原理 用I/O口实现键盘接口 显示接口基本原理 基本结构和特点 基本原理 LCD种类 市面上出售的LCD的类…

python怎么样将一段程序无效掉

1、python中可以用注释屏蔽一段语句&#xff0c;具体方法如下&#xff0c;首先打开一段python的示例程序&#xff1a; 2、然后单行注释的方法是在语句前面加上#&#xff0c;程序运行后添加注释的地方的语句会被自动跳过&#xff0c;这里可以看到将打印变量a的语句添加注释就没有…

STM32F1+HAL库+FreeTOTS学习5——内核中断管理及中断控制函数

STM32F1HAL库FreeTOTS学习5——中断管理和临界段代码保护 中断简介中断优先级寄存器拓展FreeRTOS中PendSV和Systick中断优先级配置三个中断屏蔽寄存器FreeRTOS中断管理函数代码验证 上一期我们学习了FreeRTOS中任务挂起与恢复&#xff0c;在中断服务程序中恢复任务过程中&#…

利用谷歌云serverless代码托管服务Cloud Functions构建Gemini Pro API

谷歌在2024年4月发布了全新一代的多模态模型Gemini 1.5 Pro&#xff0c;Gemini 1.5 Pro不仅能够生成创意文本和代码&#xff0c;还能理解、总结上传的图片、视频和音频内容&#xff0c;并且支持高达100万tokens的上下文。在多个基准测试中表现优异&#xff0c;性能超越了ChatGP…

竞赛选题 协同过滤电影推荐系统

文章目录 1 简介1 设计概要2 课题背景和目的3 协同过滤算法原理3.1 基于用户的协同过滤推荐算法实现原理3.1.1 步骤13.1.2 步骤23.1.3 步骤33.1.4 步骤4 4 系统实现4.1 开发环境4.2 系统功能描述4.3 系统数据流程4.3.1 用户端数据流程4.3.2 管理员端数据流程 4.4 系统功能设计 …

【Proteus】按键的实现『⒉种』

&#x1f6a9; WRITE IN FRONT &#x1f6a9; &#x1f50e; 介绍&#xff1a;"謓泽"正在路上朝着"攻城狮"方向"前进四" &#x1f50e;&#x1f3c5; 荣誉&#xff1a;2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2222年获评…

13-错误-ERROR: duplicate key value violates unique constraint “ux_xxx“

13-错误-ERROR: duplicate key value violates unique constraint “ux_xxx” 更多内容欢迎关注我&#xff08;持续更新中&#xff0c;欢迎Star✨&#xff09; Github&#xff1a;CodeZeng1998/Java-Developer-Work-Note 技术公众号&#xff1a;CodeZeng1998&#xff08;纯纯…

Vite: Bundler实现JavaScript的AST解析器—词法分析、语义分析

概述 基于前文&#xff0c;我们写了一个迷你版的 no-bundle 开发服务&#xff0c;也就是 Vite 开发阶段的 Dev Server&#xff0c;而在生产环境下面&#xff0c;处于页面性能的考虑&#xff0c;Vite 还是选择进行打包(bundle)&#xff0c;并且在底层使用 Rollup 来完成打包的过…

liunx清理服务器内存和日志

1、查看服务器磁盘占用情况 # 查看磁盘占用大小 df -h 2、删除data文件夹下面的日志 3、查看每个服务下面的日志输出文件&#xff0c;过大就先停掉服务再删除out文件再重启服务 4、先进入想删除输入日志的服务文件夹下&#xff0c;查看服务进程&#xff0c;杀掉进程&#xff…