Effective C++ 类与函数的设计和申明

在看《Effective C++》这本书的过程中,我无数次的发出感叹,这他妈写得太好了,句句一针见血,直接说到点上。所以决定把这本书的内容加上自己的理解写成5篇博客,我觉得不管你是否理解这些条款,都值得你先记下来。下面的索引对应的是书中的章节。

18:努力让接口完美且最小化

19:区分member functions,non-member functions和friend functions三者

20:避免将data member放在公开接口中

21:尽量使用const

22:尽量使用 pass-by-refernece,少用pass-by-value

18:努力让接口完美且最小化

为了客户端的方便调用,接口中可能会定义很多方法,而其中可能右很多方法是多余或是重复的,这样会导致接口中方法太多,让用户迷失在一堆的方法中,而且大型接口不易维护,长长的class定义导致头文件很长,会增大编译的时间。但是也不必太过吝啬方法的个数,如果加入一个member function会是class更好用,会是增加一个member function能减少客户端的错误,那都是这些方法都是成为接口一份子的理由。

19:区分member functions,non-member functions和friend functions三者

member function可以是虚函数而non-member function不可以,如果一个函数必须是动态绑定的那么他就必须是虚函数,就必须是memberfunction,虚函数能实现动态绑定是因为子类可以根据自己的需要重写父类的虚方法实现动态绑定,而non-member function不可能被重写。Friend function是独立于class的,他只是可以访问class的私有成员,如果一个方法不需要访问一个class的私有成员,就不应该让这个方法称为这个类的friend function。

class Rational
{public:Rational(int numerator=0,int denominator=1);int numerator()const;int denominator() const;const Rational operator*(const Rational& rhs)const;private:...
};

上面这个类表示一个分数,分数的加减乘除的方法都没有提供,那我们该以什么样的方式实现这些操作呢,是member function还是non-member function还是friend function呢?

第一直觉就是这些操作是属于Rational的应该是member function,那么我们就新增一个关于乘法的public member function,就是下面这个样子:

const Rational operator*(const Rational& rhs)const;

简单的介绍为什么是这个样子。先解释3个const,第一个const表示方法的返回为const,就是禁止我们对一个乘法赋值,如禁止a*b=3;第二个const表示在这个方法中不能修改rhs中任何成员的值,第三个const表示这个方法是const方法,在这个方法中不能修改调用这个方法的对象的数据成员。Const还有其他很多作用将会在下一个条款中介绍。

返回值为什么是by value?首先我们必须用一个变量来存乘法的结果值,我们不能在方法中构造一个局部变量,然后返回他的引用,因为这个方法执行完后,局部变量会被自动回收。

参数为什么为引用类型?一句话尽量用by reference代替by value,条款22专门讲述这个问题。这个返回值返回使用by value是没有其他办法了,你必须用一个变量来存放结果值。有了这个方法我们就可以进行乘法操作了。

Rational oneHalf(1,2),twoFive(2,5);

Rational result=oneHalf*twoFive;//没有问题

Result=oneHalf*3;//没有问题,在类型不匹配的时候编译器会一直寻找隐式类型转换的方法,直到找不到报错,由于构造函数的两个参数都有默认值,所以可以发生隐式类型转换,3相当于Rational(3,1),于是不会出现任何问题。

乘法的交换律告诉我们:a*b=b*a;于是我想oneHalf*3可以写成3*oneHalf,但是对不起不行,

3*oneHalf相当于3.operator(oneHalf),在这个3是操作对象,不会发生任何类型转换,而oneHalf是参数,于是编译器寻找将Rational转换成int(假设3为int类型)的方法,当然是没有啦。为了实现Rational和int类型的任意操作用member function是不可能啦,于是用non-member function,于是写下乘法的方法如下:

const Rational operator*(const Rational& lhs,const Rational& rhs)
{return Rational(lhs.numerator()*rhs.numerator(),lhs.denominator()*rhs. denominator());
}

Non-member function当然不需要用cosnt修饰,将乘法结果存在一个匿名变量中,如果编译有优化的话,这个结果将直接存在接收这个方法的变量中。有了这个方法,3*oneHalf就没有任何问题了,现在3被隐式转换成Rational(3,1)了,不会出现任何问题,如果你不想出现隐式类型转换,就在构造函数的前面加上explicit。最后要考虑的是是否需要将这个方法称为Rational的friend function,当然是不需要,因为让他称为friend function没有任何帮助,多一个没有任何帮助的朋友有这个必要吗?

20:避免将data member放在公开接口中

将data member设为private,然后用member function实现读写操作,对于用过面向对象语言的朋友都知道,就不废话了。如果这些方法只是返回data member的话,可以让这些方法称为inline,就可以节省方法调用带来的性能损失。

21:尽量使用const

关于这一条上面也提到一些。

Const修饰方法的返回值表示不可以直接对这个方法进行赋值;

Const修饰方法表示在这个方法中不能修改data member;

Const修饰参数表示这个参数在这个方法中不能修改;

常量性的不同也可以实现方法的重载,常量对象只能调用对应的常量方法。非常量对象可以调用常量方法。

22:尽量使用 pass-by-refernece,少用pass-by-value

先看一个类,然后看两个方法的对比,其他的废话就多说了,因为C#引用类型默认的是pass-by-reference,而C++任何类型默认的都是pass-by-value。

class DataItem
{public:DataItem(){cout<<"    constructor DataItem"<<endl;}~DataItem(){cout<<"    ~destructor DataItem"<<endl;}DataItem(const DataItem& item){cout<<"    constructor DataItem"<<endl;value=item.value;text=item.text;//*this=item;//这句的作用等同于上面两句,但是它会调用operator= ,就多了一次方法调用
        }const DataItem&  operator=(const DataItem&  item){cout<<"    operator= DataItem"<<endl;text=item.text;value=item.value;return *this;}DataItem* operator&(){return this;}const DataItem* operator&()const{return this;}int GetValue(){return value;}void SetValue(int val){value=val;}string& GetText(){return *text;}void SetText(string* txt){text=txt;}private : int value;string* text;
};

两个对比方法及测试代码:

DataItem getDataItemByValue(DataItem item)
{return item;
}const DataItem& getDataItemByReference(const DataItem& item)
{return item;
}
void TestDataItem()
{ DataItem item; cout<<"getDataItemByValue start:"<<endl;getDataItemByValue(item);cout<<"getDataItemByValue end"<<endl;cout<<endl;cout<<"getDataItemByReference start:"<<endl;getDataItemByReference(item);cout<<"getDataItemByReference end"<<endl;
}

结果截图:

从结果中我们看到pass-by-value多调用两次构造函数,两次析构函数,还有对象的数据成员的构造和析构,损失的确是很惨重。

废话真的不想多少了,写这种博客真他妈累,有不想写的念头了,还是把时间放在实战练习中吧。

Effective c++这个系列暂时暂停。

Effective C++系列:

Effective C++构造函数析构函数Assignment运算符

  Effective C++ 类与函数的设计和申明

   Effective C++面向对象与继承

作者:陈太汉

博客:http://www.cnblogs.com/hlxs/

本文转自啊汉博客园博客,原文链接:http://www.cnblogs.com/hlxs/archive/2012/07/22/2603538.html

Effective C++ 类与函数设计和申明相关推荐

  1. effective c++:virtual函数在构造函数和析构函数中的注意事项

    effective c++:virtual函数在构造函数和析构函数中的注意事项 如不使用自动生成函数要明确拒绝 对于一个类,如果你没有声明,c++会自动生成一个构造函数,一个析构函数,一个copy构造 ...

  2. 不为人知的 35 个 More Effective C++ 改善编程与设计的最佳方法 | 原力计划

    作者 | fengbingchun 责编 | 屠敏 出品 | CSDN 博客 Scott Meyers大师Effective三部曲:Effective C++.More Effective C++.E ...

  3. linux线程创建 类函数吗,linux多线程创建时使用类成员函数作为参数

    实际上所有线程都是用来处理C函数的,而不是C++类成员函数.标准库中提供一个API函数,这个函数以回调函数指针作为线程的执行代码并在单独的线程中调用回调函数.问题是在这样的线程库中不能创建执行对象成员 ...

  4. python中扑克牌类设计_Python类的基础设计、使用

    类是事物功能和属性的抽象,对象是类的实例化.就像人人都知道小汽车可以干什么什么的,但光想小汽车如何好是没有用的,得开车或坐才能感受到小汽车的好.人脑里的小汽车是抽象的,即类,而坐上的某辆车是小汽车的实 ...

  5. 基类成员函数和派生类成员函数不构成重载

    基类成员和派生类成员的名字一样时会造成遮蔽,这句话对于成员变量很好理解,对于成员函数要引起注意,不管函数的参数如何,只要名字一样就会造成遮蔽.换句话说,基类成员函数和派生类成员函数不会构成重载,如果派 ...

  6. CRC校验原理及CRC-8简单校验函数设计

    CRC校验原理及CRC-8简单校验函数设计 CRC为循环冗余校验码,是一种常用的.具有检错.纠错能力的校验码.通常发送方在发送的数据之后,附上其CRC校验码.接收方收到数据后,也做同样的CRC校验,得 ...

  7. 指向 类成员函数 的 函数指针

    原文:http://www.vckbase.com/document/viewdoc/?id=1818 一.成员函数指针的用法 在C++中,成员函数的指针是个比较特殊的东西.对普通的函数指针来说,可以 ...

  8. 笔记②:牛客校招冲刺集训营---C++工程师(面向对象(友元、运算符重载、继承、多态) -- 内存管理 -- 名称空间、模板(类模板/函数模板) -- STL)

    0618 C++工程师 第5章 高频考点与真题精讲 5.1 指针 & 5.2 函数 5.3 面向对象(和5.4.5.5共三次直播课) 5.3.1 - 5.3.11 5.3.12-14 友元 友 ...

  9. 基础测绘计算函数设计(坐标正反算、交会计算)

    基础测绘计算函数设计 1.实验内容 1.1 坐标正反算 1.2 交会计算 1.2.1 角度前方交会 1.2.2 角度后方交会 1.2.3 距离交会 2.界面展示 2.1主界面 2.2 坐标正反算 2. ...

最新文章

  1. MATLAB实例路径与安装路径
  2. Bootstrap -- 插件: 按钮状态、折叠样式、轮播样式
  3. canva画图 图片居中裁剪_Canvas裁剪图片(截选框可拖拽)
  4. mysql 连接池的作用,数据库连接池介绍、主要参数设置、作用
  5. Java四大知识点讲解,初学者必看
  6. 2012 IBM软件技术峰会:IBM与开发者谈四大热门领域看法
  7. 官宣,11月8号,.NET6+64位VS璀璨面世!
  8. ASP.NET Core 中文文档 第三章 原理(1)应用程序启动
  9. java轮询文件停止线程,java 运行多线程轮询时,外部停止轮询
  10. 如何培养编程所需要的逻辑思维?
  11. 永久把linux系统chrome的user agent 改成win10系统的
  12. 拓端tecdat|使用R语言进行多项式回归、非线性回归模型曲线拟合
  13. Web---演示Servlet的相关类、下载技术、线程问题、自定义404页面
  14. 国家地表水水质自动监测站坐标位置数据(共1946个点位,含断面名称、所在省份、所在城市、经度、纬度、所在河流、所在流域、断面属性、介绍、属地管理)
  15. 打开计算机出现酷我音乐删不掉,删除 “我的电脑” 里的 “酷我音乐” 快捷方式...
  16. 深入理解Flash的沙箱
  17. 全角字符空格(可复制使用)
  18. 吐血整理:顶级程序员工具集和在线网站大集合~
  19. 大豆SNP位点信息查找V2.1版本
  20. 软件测试周刊(第75期):唯有平视,才能看见真实的自己。

热门文章

  1. Eclipse复制或修改项目后,把项目部署后发现还是原来的项目名称
  2. Iterator(迭代器)接口 --对象循环遍历
  3. 坚持的力量 第十五篇
  4. Android数据手册02:android.permission权限请求汇总
  5. Android图形---OpenGL(二)
  6. 《iPhone开发基础教程》第13章 我在哪里?使用Core Location定位功能
  7. .NET基础示例系列之六:委托及事件
  8. mac开启sshserver
  9. 画布之轮播图片HTML5,Axure原型设计之轮播图
  10. php 去除实体,php – 如何在学说中正确关闭实体管理器