考虑有理数的class:

class Rational
{
public:
    Rational(int numerator = 0, int denominator = 1);
protected:
private:
    int n, d;
    friend Rational operator*(const Rational& lhs, const Rational& rhs);
};

by value版的operator*, 你可能想改而返回reference,以根除pass-by-value的种种邪恶,省掉构造和析构,但是,

所谓reference只是个名称,代表某个既有对象,任何时候看到一个reference声明式,你都应该立刻问自己,它的另一个名称是什么?

如果operator*要返回一个reference指向如此数值,他必须自己创建那个Rational对象。

const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
    Rational result(lhs.n * rhs.n, lhs.d * rhs.d); //警告!糟糕的代码!
    return result;
}

你的目标是要避免调用构造函数,而result却必须像任何对象一样地由构造函数构造起来。更严重的是result是一个local对象,而local对象在函数退出前被销毁了。因此这个返回的reference指向的是一个“从前的”Rational;如今已经被销毁了。任何调用者甚至对此函数的返回值做任何一点点运用,都会陷入“无定义行为”的恶地。

于是考虑在heap内构造一个对象, 并返回reference指向它。

const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
    Rational* result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d); //更糟的代码!
    return *result;
}

你还是必须付出一个构造函数调用的代价,此外又有另一个问题:谁该对着被你new出来的对象实施delete?

Rational w, x, y, z;

w = x * y * z; //与operator*(operator*(x, y), z)相同

同一个语句调用了两次operator*, 因而两次使用new,也就需要两次delete。但却没有合理的办法让operator*使用者进行delete调用, 因为没有合理的办法让他们取到operator*返回的reference背后隐藏的那个指针。这绝对导致资源泄漏。

或许你想到一种办法可以避免任何构造函数被调用。“让operator*返回reference指向一个被定义于函数内部的static Rational对象”

const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
    static Rational result; //又是一堆烂代码。
    result = ...; //lhs*rhs
    return result;
}

就像所有用上static对象的设计一样, 这个也立刻造成我们对多线程安全性的疑虑。只是显而易见的弱点。更深层的瑕疵:

bool operator=(const Rational& lhs, const Rational& rhs);
Rational a, b, c, d;
if ((a*b) == (c*d)){
    ...
}else{
    ...
}

表达式if ((a*b) == (c*d))总是被核算为true, 不论a,b, c,d是什么。

在operator==被调用前,已有两个operator*调用式起作用,每个都返回operator*内部定义的static Rational对象的reference,这个将“operator*内的static Rational对象值”和“operator*内的static Rational对象值”比较,不相等才怪。(虽然两次调用operator*都改变了static Rational对象的值, 但是返回的reference, 调用端看到的永远是static Rational的现值)。

一个“必须返回新对象”的函数的正确写法:就让那个函数返回一个新对象。

inline const Rational operator*(const Rationa& lhs, const Rational& rhs)

{

return Rational(lhs.n * rhs.n, lhs.d * rhs.d);

}

绝不要返回pointer或reference指向一个local stack对象, 或返回reference指向一个heap-allocated对象, 或返回pointer或reference指向一个local static对象而有可能同时需要多个这样的对象。条款4已经为“在单线程环境中合理返回reference指向一个local static对象”提供了一份设计实例(singleton)

转载于:https://www.cnblogs.com/lidan/archive/2012/01/17/2325108.html

effective C++ 条款 21:必须返回对象时别妄想返回其reference相关推荐

  1. 条款23: 必须返回一个对象时不要试图返回一个引用

    据说爱因斯坦曾提出过这样的建议:尽可能地让事情简单,但不要过于简单.在C++语言中相似的说法应该是:尽可能地使程序高效,但不要过于高效. 一旦程序员抓住了"传值"在效率上的把柄(参 ...

  2. Effective C++ 条款21

    必须返回对象时.别妄想返回其reference 我们上节分析了对象引用传递的优点,如今说明函数返回引用对象带来的种种坏处. 先来一段代码: class Rational{ public:Rationa ...

  3. 条款12:复制对象时勿忘其每一个部分

    设计良好的面向对象系统会将对象的内部封装起来,只留两个函数负责对象拷贝,即copy构造函数与copy assignment操作符.编译器会在必要的时候为类创建coping函数,并说明这些"编 ...

  4. Effective C++条款4:确认对象钱已经被初始化

    要点: 构造函数内赋值和使用初始化列表区别在于 1).因为const成员只能被初始化,不能被赋值,所以必须利用构造函数初始化列表进行初始化 eg: class Point { public: Poin ...

  5. Effective C++条款粗略总结

    文章目录 Effective C++ 1.类/结构体 2.资源管理 3.实现 4.模板与泛型编程 5.定制new和delete 6.其他 Effective C++ 1.类/结构体 1.把C++看成一 ...

  6. Effective C++条款(第三版-侯杰译)

    条款一:视C++为一个语言联邦 [C++高效编程守则视情况而变化,取决于你使用的C++哪一部分] 条款二:尽量以const,enum,inline替换#define [对于单纯变量,最好以const对 ...

  7. 浅析 SpringMVC 中返回对象的循环引用问题

    问题发现 今天这个话题还是比较轻松的,可能很多朋友也都遇到过这个问题. @RestController.@ResponseBody 等注解是我们在写 Web 应用时打交道最多的注解了,我们经常有这样的 ...

  8. Java基础:什么是返回对象

    在学习Java的时候总会遇到一些问题,今天就给大家说下什么是返回对象. 方法能够返回任何类型的数据,包括你创建的类的类型.例如,在下面的程序中,incrByTen()方法返回一个对象,在该对象中的值a ...

  9. Effective C++ 条款13 以对象管理资源

    // // main.cpp // 条款13:以对象管理资源 // // Created by 于磊 on 2018/7/8. // Copyright © 2018 于磊. All rights r ...

最新文章

  1. exchange 2013 lesson 6 CAS HA installing
  2. 计算机应用基础1,计算机应用基础1
  3. 生成chm文档工具- Sandcastle -摘自网络
  4. centos如何界面操作mysql_【mysql】centos7下mysql的安装以及基本操作
  5. 接口没获取到就被使用_使用CompletableFuture时,那些令人头疼的问题
  6. Google Chrome(谷歌浏览器)安装使用
  7. Python-振动信号加入噪声-代码实现
  8. 解决设置easyUI tabs onSelect事件时提示tabs未定义的方法
  9. 美丽的余霞风景mac高清动态壁纸
  10. 禁用win10笔记本键盘
  11. 关于websocket的http无法升级到ws请求的错误The HTTP response from the server [404] did not permit the HTTP upgrad
  12. 金融业移动管理驾驶舱产品功能介绍
  13. 基于jsp,ssm进销存管理系统
  14. uniapp php接口如何写,uniapp怎么请求接口
  15. bootstrap4日期时间选择器插件
  16. 使用PowerDesigner生成数据库建表语句含有clustered关键字
  17. citespace使用教程系列文章:一、citespace安装和关键词共现图
  18. 罗斯蒙特变送器三种常见故障
  19. Python发送邮件及踩坑总结
  20. 关于神舟战神z7m笔记本使用U盘装载Ubuntu系统的一点心得

热门文章

  1. 【网工必备】网络端口号大全......
  2. 【教程】Linux下MySQL 8.0安装配置
  3. 韵达基于云原生的业务中台建设 | 实战派
  4. 阿里云引领云原生进化,智能、互联、可信三位一体
  5. Serverless 解惑——函数计算如何访问 PostgreSQL 数据库
  6. 51个国家,2372名选手,20万奖金池,阿里全球调度算法大赛收官
  7. 弗洛伊德算法_弗洛伊德算法
  8. docker 镜像_Docker镜像分层
  9. matlab中predictor怎么填,在MATLAB中求解非線性有限元
  10. linux怎么复制手册,程序员的Linux上手手册(2) - 基础文件操作命令