条款十五: 让operator=返回*this的引用
c++程序员经常犯的一个错误是让operator=返回void,这好象没什么不合理的,但它妨碍了连续(链式)赋值操作,所以不要这样做。
一般情况下几乎总要遵循operator=输入和返回的都是类对象的引用的原则,然而有时候需要重载operator=使它能够接受不同类型的参数。例如,标准string类型提供了两个不同版本的赋值运算符:
string& // 将一个string
operator=(const string& rhs); // 赋给一个string string& // 将一个char*
operator=(const char *rhs); // 赋给一个string
请注意,即使在重载时,返回类型也是类的对象的引用。
采用缺省形式定义的赋值运算符里,对象返回值有两个很明显的候选者:赋值语句左边的对象(被this指针指向的对象)和赋值语句右边的对象(参数表中被命名的对象)。哪一个是正确的呢?
例如,对string类(假设你想在这个类中写赋值运算符,参见条款11中的解释)来说有两种可能:
string& string::operator=(const string& rhs)
{
...
return *this; // 返回左边的对象
}
string& string::operator=(const string& rhs)
{
...
return rhs; // 返回右边的对象
}
首先,返回rhs的那个版本不会通过编译,因为rhs是一个const string的引用,而operator=要返回的是一个string的引用。
看起来这个问题很容易解决——只用象这样重新声明operator=:
string& string::operator=(string& rhs) { ... }
这次又轮到用到它的应用程序不能通过编译了!再看看最初那个连续赋值语句的后面部分:
x = "hello"; // 和x.op = ("hello"); 相同
因为赋值语句的右边参数不是正确的类型——它是一个字符串,不是一个string——编译器就要产生一个临时的string对象(通过stirng构造函数——参见条款m19)使得函数继续运行。就是说,编译器必须产生大致象下面这样的代码:
const string temp("hello"); // 产生临时string
x = temp; // 临时string传给operator=
编译器一般会产生这样的临时值(除非显式地定义了所需要的构造函数——见条款19),但注意临时值是一个const。这很重要,因为它可以防止传递到函数内的临时值被修改。
现在我们就可以知道如果string的operator=声明传递一个非const的stirng参数,应用程序就不能通过编译的原因了:对于没有声明相应参数为const的函数来说,传递一个const对象是非法的。这是一个关于const的很简单的规定。
所以,结论是,这种情况下你将别无选择:当定义自己的赋值运算符时,必须返回赋值运算符左边参数的引用,*this。如果不这样做,就会导致不能连续赋值,或导致调用时的隐式类型转换不能进行(字符串常量转为const string),或两种情况同时发生。
若返回rhs,则rhs必须是非const的,但是隐式类型转换要求rhs是const的。
条款十五: 让operator=返回*this的引用的更多相关文章
- 条款10:让operator=返回一个reference to *this
例如对象x,y,z.要实现连锁赋值(假设operator=已经重载过了):x = y = z,那么operator=则必须返回一个*this. 注意这个条款不仅仅适合于operator=,对于oper ...
- 条款10:令operator= 返回一个reference to *this
关于赋值,可以写成连锁形式: int x, y, z; x = y = z = 15; //赋值连锁形式 赋值采用右结合律,故上述赋值被解析为: x = (y = (z = 15)); 为了实现连锁赋 ...
- 条款10:令operator=返回一个*this的引用
为了编程的简洁性,有时候需要串联赋值,如:x = y = z = 15; 由于赋值采用右结合,因此上述语句被解释为:x = (y = (z = 15)); 为了实现串联赋值,复制操作符函数必须返回一个 ...
- Effective C++ 条款10:令operator= 返回一个reference to *this
class Widget { public: ... Widget& operator+=(const Widget& rhs) // 返回类型是个reference,指向当前对象 { ...
- 条款十六: 在operator=中对所有数据成员赋值
当涉及到继承时,派生类的赋值运算符也必须处理它的基类成员的赋值!否则,当派生类对象向另一个派生类对象赋值时,只有派生类部分赋值了.看看下面: class base { public: ): x(ini ...
- 条款10:令operator=返回一个reference to * this(Have assignment operators return a reference to *this)
NOTE: 1.令赋值(assignment)操作符返回一个reference to *this. 2.此协议适用于所有赋值相关的运算比如:+= -= *=....
- 读书笔记_Effective_C++_条款十五:在资源类管理类中提供对原始资源的访问
void f(int* a) { cout <<* a << endl; } int main() { shared_ptr<int> p(new int(3)); ...
- 十五个常用的jquery代码段【转】
好的文章顶一个 回到顶部按钮 通过使用 jQuery 中的 animate 和 scrollTop 方法,你无需插件便可创建一个简单地回到顶部动画: 1 // Back to top 2 $('a.t ...
- 十五个常用的jquery代码段
十五个常用的jquery代码段 回到顶部按钮 通过使用 jQuery 中的 animate 和 scrollTop 方法,你无需插件便可创建一个简单地回到顶部动画: 1 // Back to top ...
随机推荐
- zabbix企业应用之windows系统安装omsa硬件监控
具体请参考 作者:dl528888http://dl528888.blog.51cto.com/2382721/1421335 大致 1.安装OMSA http://zh.community.de ...
- JAVA 学习笔记 - 反射机制
1. JAVA反射机制的概念 2. 怎样实例化一个 Class对象 Class.forName(包名.类名); 对象.getClass(); 类.class; ================== ...
- H3C AR28-31路由器组网实验
接线图 可以发现PC1和PC2不在一个网段上,如果不靠路由器就不可能ping,所以要用路由器组网 接线步骤 串行线连接路由器1与路由器2 以太网线连路由器以太网口 与 交换机接口 计算机网线连交换机口 ...
- flask的基本搭建
from flask import Flask app = Flask(__name__) @app.route("/")def index(): return "ok& ...
- day21-5 类的多态与多态性
类的多态与多态性 多态 多态指的是一类事物有多种形态,如动物有多种形态:人.狗.猪 import abc class Animal(metaclass=abc.ABCMeta): # 同一类事物:动物 ...
- KMP算法介绍
简介 KMP算法是D.E.Knuth.J.H.Morris和V.R.Pratt共同提出的,称之为Knuth-Morris-Pratt算法,简称KMP算法.该算法与Brute-Force算法相比有较大改 ...
- jquery中ajax原生代码的分析模仿
function ajax(obj){ var defaults = { url: "#", data: {}, type: ...
- [Python3网络爬虫开发实战] 3.1.2-处理异常
前一节我们了解了请求的发送过程,但是在网络不好的情况下,如果出现了异常,该怎么办呢?这时如果不处理这些异常,程序很可能因报错而终止运行,所以异常处理还是十分有必要的. urllib的error模块定义 ...
- [Python3网络爬虫开发实战] 1.8.3-Scrapy-Splash的安装
Scrapy-Splash是一个Scrapy中支持JavaScript渲染的工具,本节来介绍它的安装方式. Scrapy-Splash的安装分为两部分.一个是Splash服务的安装,具体是通过Dock ...
- Python:socket实现ftp程序
刚开始学习socket编程,还不是特熟练,码了好长时间,中间遇到许多问题,记录一下用socketserver写ftp server端: #!/usr/bin/env python import soc ...