【C++ 】string类:深拷贝与浅拷贝解析

【C++ 】string类操作全解析-CSDN博客

1.stirng类的模拟实现

1.1 经典的string类问题

上面已经对string类进行了简单的介绍,大家只要能够正常使用即可。在面试中,面试官总喜欢要求自己来模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数。大家看下以下string类的实现是否有问题?

cpp 复制代码
// 为了和标准库区分,此处使用String 
class String 
{ 
public:
    /*String()  
    :_str(new char[1])  
    {*_str = '\0';}  
    */  
    //String(const char* str = "\0") 错误示范  
    //String(const char* str = nullptr) 错误示范 
    String(const char* str = "") 
    {  
    // 构造String类对象时,如果传递nullptr指针,可以认为程序非  
        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!!!");  
    String s2(s1); 
}

说明:上述String类没有显式定义其拷贝构造函数与 赋值运算符 重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块 内存 空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。

String(const char* str = "") 中,str 的类型必须是 const char*,不能去掉 const

代码中默认参数 "" 是 字符串字面量(比如 "hello""" 这类用双引号包裹的内容),在 C++ 中: 字符串字面量存储在程序的「只读数据段」(不能被修改),其类型本质是 const char[](常量字符数组)。

当把字符串字面量赋值给指针时,编译器会自动将其转换为 const char*(指向常量的指针)------ 这是为了防止通过指针修改只读内存中的内容(否则会触发「未定义行为」,比如程序崩溃)。

1.2 浅拷贝

浅拷贝 的核心问题(多对象共享资源 + 重复释放)

浅拷贝 :也 称位 拷贝,编译器只是将对象中的值拷贝过来 。如果对象中管理资源 ,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规

就像一个家庭中有两个孩子,但父母只买了一份玩具,两个孩子愿意一块玩,则万事大吉,万一不想分享就你争我夺,玩具损坏。

可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。父母给每个孩子都买一份玩具,各自玩各自的就不会有问题了

重点:

当我们用一个 String 对象拷贝构造另一个对象时,如果没有自己实现拷贝构造函数,编译器会自动生成「浅拷贝构造函数」。

浅拷贝的本质是「位拷贝」------ 把原对象的所有成员变量的值,逐字节拷贝到新对象,包括指向资源的指针(_str)。

图解:

cpp 复制代码
String s1("hello");  // 1. 创建s1,申请内存存"hello"
String s2 = s1;       // 2. 浅拷贝:用s1构造s2(编译器自动生成)

拷贝前(s1 单独存在):

cpp 复制代码
s1:
  _str: 0x100(动态内存地址)
  _size: 5
  _capacity: 5
  ↓
  0x100内存:['h','e','l','l','o','\0']  // s1管理的资源

浅拷贝后(s2 与 s1 的状态):

cpp 复制代码
s1:                  s2:
  _str: 0x100         _str: 0x100  // 指针值相同!指向同一块内存
  _size: 5            _size: 5     // 数值拷贝
  _capacity: 5        _capacity: 5 // 数值拷贝
  ↓
  0x100内存:['h','e','l','l','o','\0']  // 两个对象共享同一份资源

浅拷贝没有为新对象(s2)重新申请新的动态内存,只是让 s2 的 _str 指针,指向了和 s1 的 _str 相同的内存地址 ------ 即 s1 和 s2 共享同一份动态内存资源。

cpp 复制代码
[创建s1] → s1._str指向0x100(内存有效)
    ↓
[浅拷贝s2] → s2._str也指向0x100(共享资源)
    ↓
[程序结束,s2先析构] → delete 0x100(内存变为无效)
    ↓
[ s1再析构 ] → 试图delete 0x100(无效内存)→ 访问违规!

1.3 深拷贝

如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。

strcpy 的工作原理是:从源字符串逐个拷贝字符(包括终止符 '\0'),直到遇到源字符串的 '\0' 才停止。

cpp 复制代码
// 深拷贝构造函数(自己实现)
String(const String& s) {
    _size = s._size;
    _capacity = s._capacity;
    _str = new char[_capacity + 1];  // 1. 为新对象申请新内存
    strcpy(_str, s._str);            // 2. 把原资源的数据拷贝过来
}
cpp 复制代码
s1:                  s2:
  _str: 0x100         _str: 0x200  // 指针指向不同内存
  _size: 5            _size: 5
  _capacity: 5        _capacity: 5
  ↓                    ↓
0x100: "hello"    0x200: "hello"  // 各自拥有独立资源
相关推荐
VIP_CQCRE5 小时前
身份证识别及信息核验 API 对接说明
java·前端·数据库
VIP_CQCRE5 小时前
Veo Videos Generation API 对接说明
java·服务器·数据库
礼拜天没时间.5 小时前
Tomcat 企业级运维实战系列(一):核心概念与基础部署
java·运维·centos·tomcat
当归10245 小时前
Ruoyi项目MyBatis升级MyBatis-Plus指南
java·tomcat·mybatis
九转苍翎6 小时前
Java内功修炼(3)——并发的四重境界:单例之固、生产消费之衡、定时之准、池化之效
java·设计模式·thread
~央千澈~6 小时前
Objective-C 的坚毅与传承:在Swift时代下的不可替代性优雅草卓伊凡
开发语言·ios·objective-c
手握风云-6 小时前
JavaEE 进阶第一期:开启前端入门之旅(上)
java·前端·java-ee
一只鱼^_6 小时前
365. 水壶问题(详解)
java·javascript·c++·leetcode·线性回归