《新标准C++程序设计》4.2-4.3(C++学习笔记13)
一、重载赋值运算符“=”
赋值运算符“=”要求左右两个操作数的类型是匹配的,或至少是兼容的。有时候希望赋值运算符两边的类型可以不匹配,比如,把一个int类型变量赋值给一个Complex对象,或把一个 char * 类型的字符串赋值给一个字符串对象,此时就需要重载赋值运算符“=”。C++规定,赋值运算符“=”只能重载为成员函数。
程序示例分析:
#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::~String() { delete[] str; } }; String& String::operator = (const char* s) { //重载“=”以使得 obj = “hello”能够成立 delete[] str; str = new char[strlen(s) + 1]; 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; }
输出结果:
Good Luck, Shenzhou 8!
二、浅拷贝和深拷贝
同类对象之间可以通过赋值运算符“=”互相赋值。如果没有经过重载,“=”的作用就是把左边的对象的每个成员都变得和右边的对象相等,即执行逐个字节拷贝的工作,这种拷贝叫作“浅拷贝” 。
经过重载,赋值号“=”的功能不再是浅拷贝,而是将一个对象中指针成员变量指向的内容复制到另一个对象中指针成员变量指向的地方,这种拷贝叫作“深拷贝” 。
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) { delete[] str; str = new char[strlen(s) + 1]; strcpy(str, s); return *this; }; ~String() { delete[] str; } };
按照这个String类的写法,下面的程序片段会引发问题
String S1, S2; S1 = “this”; S2 = “that”; S1 = S2;
如不定义自己的赋值运算符,那么S1=S2实际上导致 S1.str和 S2.str指向同一地方。
如果S1对象消亡,析构函数将释放 S1.str指向的空间,则S2消亡时还要释放一次,不妥。
另外,如果执行 S1 = "other";会导致S2.str指向的地方被delete
因此要在 class String里添加成员函数:
String & operator = (const String & s) { delete [] str; str = new char[strlen( s.str)+1]; strcpy( str,s.str); return * this; }
Question1:
考虑下面语句是否会有问题?
String s; s = "Hello"; s = s;
解决办法:
解决办法: String & operator = (const String & s){ if( this == & s) return * this; delete [] str; str = new char[strlen(s.str)+1]; strcpy( str,s.str); return * this; }
Question2:
对 operator = 返回值类型的讨论
void 好不好?
String 好不好?
为什么是 String &
对运算符进行重载的时候,好的风格是应该尽量保留运算符原本的特性
考虑
a = b = c;
和
(a=b)=c; //会修改a的值
分别等价于:
a.operator=(b.operator=(c)); (a.operator=(b)).operator=(c);
Question3:
上面的String类是否就没有问题了?
为 String类编写复制构造函数的时候,会面临和 = 同样的问题,用同样的方法处理。
String( String & s) { str = new char[strlen(s.str)+1]; strcpy(str,s.str); }
Question4:
以下说法正确的是:
A) 成员对象都是用无参构造函数初始化的
B) 封闭类中成员对象的构造函数先于封闭类的构造函数被调用
C) 封闭类中成员对象的析构函数先于封闭类的析构函数被调用
D) 若封闭类有多个成员对象,则它们的初始化顺序取决于封闭类构造函数中的成员初始化列表