c++ 提供了变量 copy (复制)方式,当然是有一定的原因。 copy 在某些场景是很有好的解决方案,但相反一面,多数情况我们也应尽量避免 copy,因为 copy 会浪费时间,降低效率。毕竟我们写 c++的目的就是追求效率。
现在我们看一下 c++ 是如何实现 copy 的,如果了解了 copy 的实现方式,我们就能更好地使用 copy 和合理地避免滥用 copy。
开始代码演示
对于基本数据类型:定义一个基本类型变量 a ,然后将变量 b 等于 a,这时变量 b 是 a 的 copy。但 a 和 b 指向不同的内存地址,所以当更改了变量 b 的值,不会影响到变量 a。
对于复合数据类型:class/struct 和基本类型是一样的,我们将 a 的 copy 一份复制给 b, a 和 b 还是指向不同内存的地址。
我们可以在堆内存中创建一个 Vector2 实例,将创建好的 Vector2 对象用内存指针地址 a。 这里b copy 的 a 指向的内存的地址,也就是 a b 都是指向一个内存地址。所以当修改了 b->x 属性值,也就是等于修改了 a->x 的属性值。可以想象我们这里,是将以一个引用赋值给另一个引用,他们都引用相同的内存地址。
接下来,进一步讨论一下 copy,现在自己来实现一个 String 类。首先我们明确一下 String 应该是由一系列字母组成的,所以在 private 定义指针 m_Buffer 用于保存 char 类型数据,再定义 m_Size 为 String 包含字母的数量。
String 构造函数接受指针 string (为 chart 类型数组)构造函数中要做两件事,第一件就是计算字符串的长度,然后赋值给 m_Size ,然后就是将 string 指针指向内存地址中的数据赋值给 m_Buffer。我们可以for循环 string 然后将 string 中内容赋值给 m_Buffer。
这里 memcpy 接受三个参数,第一个参数为复制的目标,第二个参数复制的源,第三参数为复制的长度。
创建好 String 的构造函数,我们还需让 String 可以输出 m_Buffer 中的内容。
这里使用 friend ,以便函数可以调用 string 类中的 private 属性 m_Buffer。
将 GetBuffer() 方法替换为 m_Buffer 以输出 m_Buffer 中的内容。
由于没有标识字符串结束(也就是字母组成的数组)这里才会有除了 jangwoo 后面的其他符号。连续内存地址中存储字符组成的字符串,最后应该有用 0 占位的内存地址。表示字符串结束。
要处理掉这个问题很简单,只需要在为 m_Buffer 分配地址时加上一个字节,即可。表示字符串结束。
由于 m_Buffer 是通过 new 关键字创建的,占用堆内存。我们需要在 String 的析构函数中将 m_Buffer 占用的内存释放掉。
创建String 类型的变量 string 赋值为 “jangwoo" ,然后 string 赋值给变量 second。试着在终端输出 string 和 second 。运行程序,发现报错了
解释一下吧,同一个类型变量 second 和 string 经过复制后,这两个变量中保存的 m_buffer 变量指向同一个内存地址。
我们为 String 类型添加一个方法,这个方法用于根据 index 获取对应位置上字符。
这里复制是浅复制,所以当执行到析构函数,就会重复释放 m_Buffer 两次,因此才报错。我们需要深复制来避免这个问题。要实现深度复制,我们需要创建一个构造函数 String 在这个构造函数我们新建 m_Buffer ,这样就不会指向一个同一个 m_Buffer 的内存地址。
网友评论