分享
 
 
 

C++ articles:Guru of the Week #4 -- Class Mechantics

王朝c/c++·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

作者:Hub Sutter

译者:plpliuly

/*此文是译者出于自娱翻译的GotW(Guru of the Week)系列文章第3篇,原文的版权是属于Hub Sutter(著名的C++专家,"Exceptional C++"的作者)。此文的翻译没有征得原作者的同意,只供学习讨论。——译者

*/

#4 类的结构(Class Mechanics)

难度:7.5/10

你对定义一个类牵涉到的具体细节熟悉多少?这次GotW不仅讨论一些写一个类时很容易犯的错误,也要讨论怎样使你写的类具有专业风格.

问题:

假设你在看一段代码.其中包含如下一个类定义.这个类定义中有几处风格差劲,还有几处是的的确确的错误.你可以找到几处,该怎样修改?

class Complex {

public:

Complex( double real, double imaginary = 0 )

: _real(real), _imaginary(imaginary) {};

void operator+ ( Complex other ) {

_real = _real + other._real;

_imaginary = _imaginary + other._imaginary;

}

void operator<<( ostream os ) {

os << "(" << _real << "," << _imaginary << ")";

}

Complex operator++() {

++_real;

return *this;

}

Complex operator++( int ) {

Complex temp = *this;

++_real;

return temp;

}

private:

double _real, _imaginary;

};

答案:

这个类定义中出现的需要修改或改进的地方远不止我们在下面要提到的几处.我们提出这个问题的目的主要是为了讨论类定义的构成(比如,"<<操作符的标准形式是什么样的","+操作符是不是应该定义为成员函数?"),而不是讨论哪个接口的设计是不是太糟糕.但是,我还是想首先提一下属于后方面的建议,作为第0个值得修改的地方:

0.既然标准库中已经有复数类为什么还要自己定义一个Complex类呢?(而且,标准库中定义的类不会有下面提及的任何一个问题,是由业界中具有多年经验的最优秀的人编写.还是谦虚一点去直接重用这些类吧)

[忠告]尽量直接使用用标准库算法.那会比你自己动手写一个要快捷,而且不易出错.

class Complex {

public:

Complex( double real, double imaginary = 0 )

: _real(real), _imaginary(imaginary) {};

1.风格问题:这个构造函数可以当作单参数函数使用,这样就可能作为一个隐式转换函数使用.但是可能并不是所有时候发生的隐式转换都是你所期望的.

[忠告]当心不易察觉的类型转换操作在你并不希望的时候发生.一个好的避免办法就是:可能的话就用explicit修饰词限制构造函数.

void operator+ ( Complex other ) {

_real = _real + other._real;

_imaginary = _imaginary + other._imaginary;

}

2.风格问题:从效率上讲,参数应该是const&类型,而且"a=a+b"形式的语句应该换成"a+=b"的语句.

[准则]尽量使用const&参数类型代替拷贝传值类型参数.

[忠告]对于算术运算,尽量使用"a op= b"代替"a = a op b".(注意有些类--比如一个别人写的类--可能通过操作符的重载改变了op和op=之间的关系)

3.风格问题:+操作符不应该定义为成员函数.如果被定义成如上述代码中的成员函数,你就只能用"a=b+1"形式的语句而不能用形如"a=1+b"的语句.这样,你就需要同时提供operator+(Complex,int)和operator+(int,Complex)的定义.

[准则]在考虑把一个操作符函数定义为成员函数还是非成员函数的时候,尽量参照下面的原则:(Lakos96:143-144;591-595;Murray93:47-49)

- 一元(单参数)操作符函数应该定义成成员函数

- =,(),[],和->操作符应该定义成成员函数

- +=,-=,/=,*=,(等等)应该定义成成员函数

- 其余的操作符都应该定义成非成员函数

4.错误:+操作符不应该改变对象自身.它应该返回包含相加结果的临时对象.请注意,为了防止类似"a+b=c"的操作,这个操作符函数返回类型应该是"const Complex"(而不仅仅是"Complex").

(实际上,上述的代码更象定义+=操作符函数的代码)

5.风格问题:一般来讲,当定义了op的操作符函数,就应该同时定义了op=操作符.因此,这儿也应该定义+=操作符.上面的代码就应当是+=的定义(但是返回类型需要修改,参看下面的讨论).

void operator<<( ostream os ) {

os << "(" << _real << "," << _imaginary << ")";

}

(注意:对于一个真正的<<操作符,你应该检查stream的当前格式标志以支持一般的用法(译者:iostream通过设置标志来对输出格式进行控制).请参考你喜欢的STL的书籍了解细节)

6.错误:操作符<<不应该定义成成员函数(参看上面讨论),而且参数应该是"(ostream&,const Complex&)".注意,正如James Kanze指出,也尽量不要把它定义成友元函数!最好把它定义成通过调用一个"print"的public成员函数来工作.

7.错误:这个操作符函数应该返回"ostream&",而且应该以"return os"结束.这样就可以支持将多个输出操作链接起来(比如"cout << a << b;").

[准则]操作符<<和>>都应该返回stream的引用.

Complex operator++() {

++_real;

return *this;

}

8.风格问题:前自增应该返回Complex&,以便调用者可以更直接的操作.(译者:其实我觉得这不仅仅是风格的问题.因为按照前自增的标准定义,应该支持"++++a"的语法,而且两次前自增都应该是对a对象的自身操作,如果返回Complex类型,那第二次前自增调用的是临时对象的前自增操作.)

Complex operator++( int ) {

Complex temp = *this;

++_real;

return temp;

}

9.风格问题:后自增应该返回"const Complex".这可以防止形如"a++++"的用法.这句话可不会象某些人想当然那样会连续对a对象作两次自增操作.

10.风格问题:如果通过前自增操作来实现后自增操作符函数将会更好.(译者:将"++_real;"替换为"++(*this);")

[准则]尽量通过前自增操作来实现后自增操作.

private:

double _real, _imaginary;

};

11.风格问题:尽量避免使用以下划线开头命名变量.我曾经也很习惯这样定义变量,就连著名的"Design Patterns"(Gamma et al)中也是这样.但是因为标准库的实现中保留了很多下划线开头标识符,如果我们要用下划线开头定义自己的变量就得记住全部已经保留的标识符以免冲突,这太难了.(既然使用下划线作为成员变量的标志容易跟保留标识符冲突,那我就在变量结尾加下划线)

好了.最后,不考虑其他一些上面没有提到的设计和风格上的缺陷,我们可以得到下面的经过修正的代码:

class Complex {

public:

explicit Complex( double real, double imaginary = 0 )

: real_(real), imaginary_(imaginary) {}

Complex& operator+=( const Complex& other ) {

real_ += other.real_;

imaginary_ += other.imaginary_;

return *this;

}

Complex& operator++() {

++real_;

return *this;

}

const Complex operator++( int ) {

Complex temp = *this;

++(*this);

return temp;

}

ostream& print( ostream& os ) const {

return os << "(" << real_

<< "," << imaginary_ << ")";

}

private:

double real_, imaginary_;

friend ostream&

operator<<( ostream& os, const Complex& c );

};

const Complex operator+( const Complex& lhs,

const Complex& rhs ) {

Complex ret( lhs );

ret += rhs;

return ret;

}

ostream& operator<<( ostream& os,

const Complex& c ) {

return c.print(os);

}

-----

(结束)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有