一、为什么要重载赋值运算符 "="?
有时候希望赋值运算符两边的类型可以不匹配
比如,把一个int类型变量赋值给一个Complex对象或把一个 char *类型的字符串赋值给一个字符串对象,此时就需要重载赋值运算符“=”
赋值运算符“=”只能重载为成员函数
#include<iostream>
using namespace std;
class String {
private:
char* str;
public:
String() :str(new char[1]) { str[0] = 0; }
const char* c_str() { return str; }
String& operator=(const char* s);//重载赋值运算符
~String() { delete[]str; }
};
String& String:: operator=(const char* s) {//赋值运算符号重载的类型是String &
//重载”=“以使得obj ="hello"能够成立
delete[] str;//
str = new char[strlen(s) + 1];//+1为了存放起始位置0
strcpy(str, s);
return *this;
}
int main() {
String s;
s = "Good Luck ";//等价于 s.operator=("Good Luck");
cout << s.c_str() << endl;
//String s2="hello!"这条语句不注释掉就会出错
s = "Shenzhou 8!";// 等价于 s.operator=("Shenzhou 8!);
cout << s.c_str() << endl;
return 0;
}
输出:
//String s2="hello!"这条语句不注释掉就会出错
因为这个等号不是赋值语句,而是初始化语句,初始化语句需要相应的构造函数,而且后面需要跟char * 的类型
二、浅拷贝与深拷贝
假如我们没有进行赋值符号的重载
声明 俩个String 的对象 s1=s2会引发一些问题,
String s1, s2;
s1 = "this";
s2 = "that";
s1=s2;
第一个问题,之前s1对象指向的地方没有办法被delete掉,成为内存垃圾
(1)如不定义自己的赋值运算符,那么S1=S2实际上导致S1.str和S2.str指向同一地方。
第二个问题,可能会造成俩次delete
(2)如果S1对象消亡,析构函数将释放S1.str指向的空间,则S2消亡时还要释放一次,不妥。
因为只能new 出来的空间只能 delete 一次;
(3)另外,如果执行S1="other";会导致S2.str指向的地方被delete
因此要在 class String里添加成员函数,
String& String:: operator=(const char* s) {//赋值运算符号重载的类型是String &
//重载”=“以使得obj ="hello"能够成立
delete[] str;//
str = new char[strlen(s) + 1];//+1为了存放起始位置0,, 为Str分配新的存储空间
strcpy(str, s); //这样就不会导致s和str指向同一个位置,因为str是新new的一个空间
return *this;
优化 1:
防止一个对象赋值给另外一个对象,而这个对象恰好又和这个赋值的对象是引用关系;
而我们的赋值重载函数第一句是delete;这样会导致自己删除自己的情况,我们需要改进
String s ;
String &x=s;
s=x;
改进:
String& String :: operator =(const String& s) {//赋值运算符号重载的类型是String &
//重载”=“以使得obj ="hello"能够成立
if (this == &s) return *this; //判断这个对象是否引用了自己
delete[] str;//
str = new char[strlen(s.str) + 1];//+1为了存放起始位置0
strcpy(str, s.str);
return *this;
}
全部代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class String {
private:
char * str;
public:
String() :str(new char[1]) { str[0] = 0; }
const char* c_str() { return str; }
String & operator=(const String& s);//重载赋值运算符
String& operator=(const char *s);//重载赋值运算符
~String() { delete[]str; }
};
String& String :: operator =(const String& s) {//赋值运算符号重载的类型是String &
//重载”=“以使得obj ="hello"能够成立
if (this == &s) return *this;
delete[] str;//
str = new char[strlen(s.str) + 1];//+1为了存放起始位置0
strcpy(str, s.str);
return *this;
}
String& String:: operator=(const char* s) {//赋值运算符号重载的类型是String &
//重载”=“以使得obj ="hello"能够成立
delete[] str;//
str = new char[strlen(s) + 1];//+1为了存放起始位置0
strcpy(str, s);
return *this;
}
int main() {
/*String s;
s = "Good Luck ";//等价于 s.operator=("Good Luck");
cout << s.c_str() << endl;
//String s2="hello!"这条语句不注释掉就会出错
s = "Shenzhou 8!";// 等价于 s.operator=("Shenzhou 8!);
cout << s.c_str() << endl;*/
String s1, s2, s3, s4;
String& s4 = s3;// 引用
s4 = s3;
s1 = "this";
s2 = "that";
s1 = s2;
return 0;
}
优化二:我们还需要考虑编写复制构造函数
为 String类编写复制构造函数的时候,会面临和=同样的问题,用同样的方法处理。
如
String s1;
String s2(s1);//如果自己没写复制构造函数这会调用缺省构造函数,导致俩个对象指向同样的一片空间,从而引发跟上面一样的问题
改进:
String(const String& s) {
str = new char[strlen(s.str) + 1]; //为str 重新开辟一个空间
strcpy(str, s.str);
}