在这个类得设计和实现中,动态内存的分配和回收是一个关键的设计部分,稍有不慎,就可能造成内存泄露,当程序的运行次数上升到一定程度,内存的使用率也就越来越高,所以在关键的每一步恰当的分配和回收内存是这个类成功的关键。
那么类得内存占用到底是怎么分配的呢?
静态成员变量:一般存放在另外一块内存区域,一个类只有一个静态成员副本,所以的对象一起共享它。
每初始化一个对象,那么都会为它分配数据成员所要占用的内存空间。那么在MyString类中,我们也只是为string分配了可以存储指针的地址,并没有分配存储具体的字符串的地址。
1.那么构造函数在初始化一个类对象时,必须使用new为对象分配存储字符串的内存空间,这个就是我们的动态内存分配。但是每一个new分配的内存空间必须通过delete来释放,我们一般通过析构函数来实现:
构造函数的动态内存分配:
MyString::MyString()
{
length = 0;
string = new char;
char *s = "/0";
memcpy(string,s,1);
++string_number;
}
MyString::MyString(const char*str)
{
length = strlen(str);
string = new char(length+1);
memcpy(string,s,length);
++string_number;
}
MyString::MyString(MyString &str)
{
length = str.length;
string = str.string;
++string_number;
}
析构函数的内存释放:
MyString::~MyString()
{
delete[]string;
--string_number;
}
2.那么在一个对象的使用过程中,通过调用其他的方法函数也会进行相应的内存的释放和重新分配:
MyString &MyString::operator+=(const MyString&str)
{
char *dest;
dest = new char[str.length+length+1];
memcpy(dest,string,length);
memcpy(dest+length,str.string,str.length+1);
delete[]string;
string = dest;
return *this;
}
MyString &MyString::operator=(const MyString&str)
{
if(&str == this)
return *this;
delete[]string;
string = new char[str.length];
length = str.length;
return *this;
}
如上在这两个函数中,返回的对象和原来对象的字符串长度不同,那么我们就必须先释放原来的内存空间,然后让字符串指针指向合适长度的一块新的内存空间进行返回。
注意:当重新定位字符串指针定位时,必须先释放原来的内存。
同时在第二个赋值运算中,不能直接通过指针赋值来返回:
delete[]string;
string = str.string;
return *this;
这样的话调用对象的string和str.string指向同一块内存区域,任何一个对像先退出调用析构函数以后,当第二个对象再调用析构函数时,这个容易引起同一块内存的两次释放,引起错误
总结:
容易涉及到内存动态分配的方法函数:
1.构造函数和析构函数
2.赋值函数(常量或者对象),运算符重载函数