C++:基于浅拷贝/深拷贝对模拟string类的一些优化

简介: C++:基于浅拷贝/深拷贝对模拟string类的一些优化

string类和日期类

前面我们已经实现了string类和日期类,这两个类有没有想过它们有什么不同?

其实答案很明显,不同的地方在于string类中涉及到内存空间开辟,而日期类只是简单的对年月日三个变量进行一些变换

那有没有内存空间开辟对实际实现代码有什么影响?看下面代码

// error
class my_string
{
public:
  my_string()
  {
    _size = 0;
    _capacity = 0;
    _str = new char[_capacity+1];
  }
  ~my_string()
  {
    delete[] _str;
  }
private:
  size_t _size;
  size_t _capacity;
  char* _str;
};
int main()
{
  my_string s1;
  my_string s2;
  s2 = s1;
  return 0;
}

上面代码是可以运行吗?答案是不可以,原因在于什么?

编译器默认生成了六个成员函数,这当中包含了赋值重载,而代码中的s2=s1在编译器看来,就是把s1对象中的三个成员赋值给s2,这个过程就是所谓的浅拷贝,s2中的数据成员和s1完全相同,因此出了函数作用域后,s2会被先析构,s1继续析构,而s2被析构后它们所指向的空间已经被释放了,s1还要继续释放,很明显这是不可以的,因此程序会报错

因此我们就得出了一个初步结论,当成员变量涉及到动态开辟内存空间的时候,如果使用的还是编译器默认的赋值重载函数,就会涉及到一个空间被析构两次导致错误,这就是浅拷贝的弊端

浅拷贝/深拷贝

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

那如何解决?就需要用到深拷贝的知识:

可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享

对于上述代码的深拷贝写法

class my_string
{
public:
  my_string()
  {
    _size = 0;
    _capacity = 0;
    _str = new char[_capacity+1];
  }
  ~my_string()
  {
    delete[] _str;
  }
  my_string& operator=(const my_string& s)
  {
    _size = s._size;
    _capacity = s._capacity;
    _str = new char[_capacity + 1];
    strcpy(_str, s._str);
    return *this;
  }
private:
  size_t _size;
  size_t _capacity;
  char* _str;
};
int main()
{
  my_string s1;
  my_string s2;
  s2 = s1;
  return 0;
}

那我们采用深拷贝的写法,就解决了这个问题,在前文string模拟实现中采用的也确实是这个方法,那这个方法可不可以进行继续优化?这就是后文要讨论的知识:如何优化深拷贝

正常版本和优化版本

对于深拷贝,上述的写法是一个正常的版本,而事实上这个版本是可以被优化的,例如可以这样优化:

my_string(const my_string& s)
    :_str(nullptr)
    ,_size(0)
    ,_capacity(0)
  {
    my_string tmp(s._str);
    swap(tmp);
  }

上面就是一种对深拷贝的优化,我们可以借助一个tmp变量,构造出一个对象,再把这个对象和*this进行交换,这样也能实现深拷贝,这就是优化版本

写时拷贝

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。

引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源

简单来说,写时拷贝就是在需要的时候再进行深拷贝,如果不进行写入就起到了提升效率的作用

相关文章
|
1月前
|
Java
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
本文深入探讨了Java中方法参数的传递机制,包括值传递和引用传递的区别,以及String类对象的不可变性。通过详细讲解和示例代码,帮助读者理解参数传递的内部原理,并掌握在实际编程中正确处理参数传递的方法。关键词:Java, 方法参数传递, 值传递, 引用传递, String不可变性。
55 1
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
|
1月前
|
C++ 容器
|
30天前
|
安全 Java 测试技术
Java零基础-StringBuffer 类详解
【10月更文挑战第9天】Java零基础教学篇,手把手实践教学!
24 2
|
1月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
21 1
|
1月前
|
数据可视化 Java
让星星月亮告诉你,通过反射创建类的实例对象,并通过Unsafe theUnsafe来修改实例对象的私有的String类型的成员属性的值
本文介绍了如何使用 Unsafe 类通过反射机制修改对象的私有属性值。主要包括: 1. 获取 Unsafe 的 theUnsafe 属性:通过反射获取 Unsafe类的私有静态属性theUnsafe,并放开其访问权限,以便后续操作 2. 利用反射创建 User 类的实例对象:通过反射创建User类的实例对象,并定义预期值 3. 利用反射获取实例对象的name属性并修改:通过反射获取 User类实例对象的私有属性name,使用 Unsafe`的compareAndSwapObject方法直接在内存地址上修改属性值 核心代码展示了详细的步骤和逻辑,确保了对私有属性的修改不受 JVM 访问权限的限制
51 4
|
1月前
|
存储 安全 Java
【一步一步了解Java系列】:认识String类
【一步一步了解Java系列】:认识String类
25 2
|
1月前
|
C++ 容器
|
1月前
|
C++ 容器
|
1月前
|
C语言 C++
深度剖析C++string(中)
深度剖析C++string(中)
47 0
|
1月前
|
存储 编译器 程序员
深度剖析C++string(上篇)(2)
深度剖析C++string(上篇)(2)
35 0