我们知道C++中类里的成员函数中构造函数和拷贝构造都是值拷贝,所以地址也是值拷贝,也就是多个对象用到了同样的一块地址,例如:
#include <iostream> using namespace std; class String { public: String(char* str) :_str(new char[strlen(str)+1]) { strcpy(_str,str); } //拷贝构造函数 ~String() { delete this->_str; } private: char* _str; }; void Test() { String s1("abcdef"); String s2(s1); } int main() { Test(); return 0; }
上面的程序中我省略掉了拷贝构造函数,系统会自动生成一个,就是值拷贝,也是浅拷贝,因此s1和s2中字符串指针指向了由new开辟出来的同一块地址,那么当出了Test()的作用域时会调用两次析构函数,那么也就是会把同一块内存释放两次,系统毫无疑问就会崩溃掉,那么解决这个问题的方法就可以用深拷贝,开辟出同样大小的一块空间,再将字符串的内容给拷贝过去。
但是,就用浅拷贝可不可以解决上述问题呢?看如下的程序:
#include <iostream> using namespace std; class String { public: String(char* s = "") :_str(new char[strlen(s)+5]) { *((int*)_str) = 1; //初始化计数器个数为1 strcpy(_str+4,s); } String(String& str) { _str = str._str; ++(*((int*)_str)); //每当增加一个对象使用这块内存时计数器就++ } ~String() { if((--(*((int*)_str))) == 0)//释放内存时只有当计数器减完后为0时才可以释放 { delete[] _str; } } String& operator=(String& str) { if(--(*((int*)_str)) == 0)//只有确保没有其他对象在使用这块空间时才可以将原来的空间释放 { delete[] _str; } _str = str._str; ++(*((int*)_str)); return *this; } private: char* _str; }; void Test1() { String s1("abcd"); String s2(s1); String s3 = s2; String s4("efgh"); String s5; s5 = s4; } int main() { Test1(); return 0; }
在上面的程序中注意到在构造函数的初始化类表中我new出了多4个字节的空间,那这4个空间是用来干什么的呢?
在用new[ ]的时候,如果用sizeof检查其字节,会发现开辟出的大小会多出4个字节,这四个字节在开辟出的空间首部,用来存储数组元素的个数,从而配合delete来释放空间。
因此,我多开辟出的4个空间就也是用来存放指向同一块空间的对象的个数的。
这样就可以在需要重复使用相同的内容时节省内存空间也避免出现内存泄露或者程序崩溃。
时间: 2024-10-07 07:08:23