赋值运算符函数在写的时候要注意四点:
经典写法(不做new异常的防御式编程):
- 返回值类型声明为该类型的引用,并且返回*this。(只有返回时引用,才可以连续赋值)
- 是否将传入的参数设置为常量引用。(保证数据不被修改的同时避免了拷贝构造)
- 是否释放自己的内存。(防止分配到新内存之后无法对原有内存再控制,导致内存泄漏)
- 是否考虑到自赋值。(当没有考虑自赋值,会导致释放掉已有内存,传入数据的内存也就被释放了,找不到赋值内容)
class cstring{
public:
cstring(){}
~csting(){}
cstring(const cstring& mystr){}//拷贝构造函数已经定义
private:
char* p_data;
}
cstring& cstring::operator =(const csting& mystr){
if(&mystr == this) return *this;
delete[] p_data;//注意数组指针的释放
p_data = NULL;
p_data = new char[strlen(mystr.p_data)+1];
strcpy(p_data,mystr.p_data);
p_data[strlen(mystr.p_data)] = '\0';
return *this;
}
这样写是没问题的。可是要注意:如果new异常?没有分配到空间,同时你先delete,已经释放了原有的空间,使得原有的数据丢失;再一个,你向没有分配到空间的NULL指针去拷贝,会导致程序崩溃。
更好的写法是使用拷贝构造函数的临时对象自动释放:
cstring& cstring::operator=(const cstring& mystr){
if(this!=&mystr){
cstring temp(mystr);
swap(temp.p_data,this);//交换两个指针所指向的空间
}
return *this;
}
交换指针的指向,然后临时对象自动释放自己的空间。不过要定义拷贝构造函数。拷贝构造函数传引用的原因:1、避免一次对象的拷贝;2、防止无休止递归调用拷贝构造函数使得栈溢出。拷贝构造函数和赋值符的区别:是否有新的对象实例产生。
C++ 为什么拷贝构造函数参数必须为引用?赋值构造函数参数也必须为引用吗?
cstring::cstring(const cstring& mystr){
if(this == &mystr) return;
p_data = new char[strlen(mystr)+1];
strcpy(p_data,mystr.p_data);
p_data[strlen(mystr)] = '\0';
}
整体的代码:
class cmystring {
public:
cmystring(char* p_str = NULL) {
my_str = p_str;
}
cmystring(const cmystring& str) {
if (&str == this) return;
my_str = new char[strlen(str.my_str) + 1];
strcpy(my_str, str.my_str);
my_str[strlen(str.my_str)] = '\0';
}
cmystring(cmystring&& str) {
if (&str == this) return;
my_str = str.my_str;
str.my_str = NULL;
}
cmystring& operator=(const cmystring& str) {
if (this != &str) {
cmystring t(str);
swap(t.my_str, my_str);
}
return *this;
}
~cmystring() {
delete[]my_str;
my_str = NULL;
}
char* my_str;
};
总结:要注意的事情。
- 除了写赋值运算符的四点要求之外,注意数组的释放是delete [] my_str;
- 拷贝构造函数也要注意一下自赋值
- 必须要写成深拷贝的形式