重载赋值运算符

1、为什么要重载赋值运算符?
预定义的赋值运算符"="要求左右两边的操作数类型是匹配的或者至少是兼容的。有时希望赋值运算符两边的操作数即使类型不兼容也能成立。此时可以通过重载赋值运算符来完成这种需求。

2、重载赋值运算符的特点

  • C++规定,只能将赋值运算符重载为成员函数类型。

3、举例

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; } //析构成员函数
};

//重载“=”以使得 obj = “hello”能够成立
String & String::operator = (const char * s)
{   
	delete [] str;
	str = new char[strlen(s)+1];
	strcpy( str, s);
	return * this;
}

int main()
{
	String s;
	s = "Good Luck," ; //等价于 s.operator=("Good Luck,");  左边是String类类型,右边是string类型。赋值符号两边虽然类型不匹配,但是由于重载了赋值运算符,它能够调用符号函数,使得编译能够通过。
	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!
*/

4、该例中存在的问题

上述代码中执行:

String s2 = "hello!";

这条语句会发生错误,因为这里的等号并不是赋值的意思,而是初始化的意思。那么如何解决这种定义时初始化的问题呢?

解决方案:
可以定义一个类型转换构造函数:

	String(char* s){
        str = new char[strlen(s)+1];
        strcpy( str, s);
    }

这样,在这行这条语句时,运行的将会是类型转换构造函数。

5、为什么将赋值运算符函数的返回值写成引用类型?

首先考虑可选的类型:

  • void
  • String
  • String

我们写符号函数应该尽量符合符号原有的用法习惯。
(1)写成void的带来的问题:

a = b = c;

如果赋值运算符的返回类型为空,那么执行该语句就会出现错误。首先,运算符重载并不改变运算符的优先级和结合性,由于赋值运算符的右结合性,因此先执行b=c,此时能够正确执行,但是执行的结果为void类型,将一个空类型赋值给String类型的a对象,显然会导致错误。但是按照我们原有的对赋值运算符的用法,它是可以被用来连等的。解决这个问题的方法是,将返回值类型设置为String,或者String&。

(2)写成String带来的问题:

(a=b)=c;

解释一:
此时由于括号“()”改变了结合顺序,所以先执行a=b,结果是将b的值赋值给a,这里没有问题。但是,因为函数的返回值类型只有为引用时返回得到的才是左值,为其他类型时返回得到是右值。右值是不可以位于赋值运算符的左侧的,因此该语句会导致结果异常。

解释二:
此时由于括号“()”改变了结合顺序,所以先执行a=b,结果是将b的值赋值给a,这里没有问题。该函数表达式调用赋值符号函数,返回一个String类型的临时变量。然后再次调用赋值符号函数,将c的值赋值给该临时变量。因此,输出a的话得到的结果仍是b的值。

这两条解释的区别在于,赋值符号函数返回的结果是左值,还是非左值。可以在编译器上跑一下是哪种结果。此时可以把=看成是一个函数,这个函数返回值是左值,那么赋值符号返回的就是左值。这个函数返回值是右值,那么赋值符号返回的就是右值。在《C++ primer》一书中提到,函数返回值只有为引用类型时,返回结果才是左值,而这里显然不是左值。所以按照这样分析,应该是解释一成立。

实际上,无论这两条解释的哪一条成立,导致的结果是:我们不想看到的效果。

6、实验验证上述解释

  • 将赋值符号函数返回值改为void类型,在Codeblock上面跑此代码。确实如同上面预想的一样。在执行连等的时候会发生意外。
  • 将赋值符号函数返回值改为String类型,在Codeblock上面跑此代码。和上述解释中任何一个都不符合,实验的结果显示,将赋值符号函数返回值更改为String类型后,甚至连一个等号的赋值都会发生意外。代码与结果如下(代码较上述代码有所优化):
#include<iostream>
#include<cstring>
using namespace std;
class String {
private:
	char * str;
public:
	String ():str(NULL) { }//无参构造函数
	const char * c_str() const{ return str; };//普通成员函数
	String  operator = (const char * s);//重载赋值运算符的符号成员函数
	~String( ) {
	if(str)
        delete [] str;
    } //析构成员函数
};

//重载“=”以使得 obj = “hello”能够成立
String String::operator = (const char * s)
{
    cout <<"operator = " << endl;
    if(str)
        delete [] str;
    if(s){
    cout <<"if s " << endl;
        str = new char[strlen(s)+1];
        strcpy( str, s);
    }else{
        str=NULL;
    }
    cout << (*this).c_str() << endl;
	return *this;
}

int main()
{
	String s,s1,s2;
	s = "Good Luck,") ; 
    cout <<"s1"<< s1.c_str() << endl;
    s1 = "shenzhou8";
    cout <<"s1:"<< s1.c_str() << endl;
    s2=s1=s;
    cout <<"s2:"<< s2.c_str() << endl;
	return 0;
}

在这里插入图片描述

posted @ 2019-11-25 19:46  江南又一春  阅读(678)  评论(0编辑  收藏  举报