赞
踩
在字符串、指针或者带有指针的对象拷贝的时候,如果只是简单做了赋值处理,不能拷贝一份指针指向的内存地址,只是拷贝了一份指向内存地址的指针,在对拷贝过的内存操作的时候就很可能带来一些错误,比如释放了拷贝过的内存,之前拷贝前的内存也会被释放,因为根本是同一个内存地址。
下面是一个基本类型的案例:
- struct Vector2 {
- float x, y;
- Vector2(float x, float y) :x(x), y(y) {}
- };
-
- int main() {
- Vector2* a = new Vector2(1.0f,2.0f);
- Vector2* b = a;
- b->x = 3.0f;
- std::cout << a->x << std::endl;
- std::cout << b->y << std::endl;
- std::cout << b->x << std::endl;
- }
可以看出,通过赋值后的指针指向,正常修改其指向值,复制对象指针指向的值也会被修改,因为在赋值时,只复制了指针,并没有赋值指向对象,下面通过一个字符串的例子进一步说明:
手写一个string类,
- class String {
- private:
- char* m_Buffer;
- unsigned int m_Size;
- public:
- String(const char* string) {
- m_Size = strlen(string);
- m_Buffer = new char[m_Size + 1];
- //需要算上终止符,加上1的长度,也可以使用strcpy函数,拷贝时会包含空终止符
- /*for (int i = 0; i < m_Size;i++) {
- m_Buffer[i] = string[i];
- }*/
- memcpy(m_Buffer, string, m_Size + 1);
- //目的、来源、大小,少写了一个\0终止符
- m_Buffer[m_Size] = 0;//手动加上终止符
- }
- ~String() {
- delete[] m_Buffer;
- }
- friend std::ostream& operator<<(std::ostream& stream, const String& string);//友元
- };
- void PrintString(String string) {
- std::cout << string << std::endl;
- }
- int main() {
- String string = "lhx";
- String second = string;
- //浅拷贝,因为复制的指针,而指针作用的是同一片内存空间
- //解决办法,深拷贝:拷贝构造函数
- second[1] = 'v';
- PrintString(string);
- PrintString(second);
- std::cin.get();
- }

second[1] = 'v';报错,因为没有对string类进行[]重载,并不能完成中括号数组的操作,在类中加上如下代码完成对[]的重载:
- char& operator[](unsigned int index) {
- return m_Buffer[index];
- }
运行后发现,输出都是lvx,并且在程序结束的时候会出线错误,说明lhx只有一个,并且被修改为lvx,而出现错误是因为在使用析构函数进行释放内存的时候,同一片内存被连续释放了。
所以需要重写深拷贝构造方法,在类中加入如下代码:
- String(const String& other) :m_Size(other.m_Size) {
- std::cout << "deep copy" << std::endl;
- m_Buffer = new char[m_Size + 1];
- memcpy(m_Buffer, other.m_Buffer, m_Size + 1);
- }//深拷贝使用代码
发现被连续copy操作被连续调用,但不再出现错误:
为解决连续调用问题,对以下代码重写:
- void PrintString(const String& string) {
- std::cout << string << std::endl;
- }
要习惯总是通过const引用去传递对象,但在某些情况下,复制可能更快。
如果不带const,可以将临时的右值传递到实际函数,这个以后再说~
以下为全部代码:
- #include<iostream>
- #include<string>
- struct Vector2 {
- float x, y;
- Vector2(float x, float y) :x(x), y(y) {}
- };
- class String {
- private:
- char* m_Buffer;
- unsigned int m_Size;
- public:
- String(const char* string) {
- m_Size = strlen(string);
- m_Buffer = new char[m_Size + 1];//需要算上终止符,加上1的长度,也可以使用strcpy函数,拷贝时会包含空终止符
- /*for (int i = 0; i < m_Size;i++) {
- m_Buffer[i] = string[i];
- }*/
- memcpy(m_Buffer, string, m_Size + 1);//目的、来源、大小,少写了一个\0终止符
- m_Buffer[m_Size] = 0;//手动加上终止符
- }
-
- //String(const String& other) :m_Buffer(other.m_Buffer), m_Size(other.m_Size) {}
- //String(const String& other) {
- // memcpy(this, &other, sizeof(String));
- //}
- //String(const String& other) = delete;
- String(const String& other) :m_Size(other.m_Size) {
- std::cout << "deep copy" << std::endl;
- m_Buffer = new char[m_Size + 1];
- memcpy(m_Buffer, other.m_Buffer, m_Size + 1);
- }//深拷贝使用代码
- ~String() {
- delete[] m_Buffer;
- }
-
- char& operator[](unsigned int index) {
- return m_Buffer[index];
- }
- friend std::ostream& operator<<(std::ostream& stream, const String& string);//友元
- };
- std::ostream& operator<<(std::ostream& stream, const String& string) {
- //stream << string.m_Buffer();
- stream << string.m_Buffer;
- return stream;
- }
- void PrintString(const String& string) {
- std::cout << string << std::endl;//如果不带const,可以将临时的右值传递到实际函数
- //总是通过const引用去传递对象,在某些情况下,复制可能更快
- }
- //void PrintString(String string) {
- // std::cout << string << std::endl;
- //}
- int main() {
- String string = "lhx";
- String second = string;//浅拷贝,因为复制的指针,而指针作用的是同一片内存空间
- //解决办法,深拷贝:拷贝构造函数
- second[1] = 'v';
- PrintString(string);
- PrintString(second);
- std::cin.get();
- }

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。