GotW #4 类技术

原文http://www.gotw.ca/gotw/004.htm
难度:7.5/10 (好高难度..怕怕)
你书写类的熟练程度怎么样?这一节关注的不只是明显的错误,还有更专业的风格。

问题:
你又在做code review。码农写了这么一段类的代码,里边有很多错误和很差的风格。你能找出多少错误并改正?
    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;
    }
;
(我找到4个错误。不知道对不对。感觉自己好菜啊。。看看答案吧)

答案:
序:(答案都有序,估计又是茫茫多的错啊。。 我好菜。。)
这个类里有非常多的错。。。本题的重点在类的机制。(比如:operator<<的标准形式是什么?operator+能作成员吗?)而不是糟糕的设计。不过,我们还是从一个很有用的批评开始吧。
0.为什么要写一个复数类?标准库里不是有现成的complex容器了吗?!(而且,顺带说一句,那里没有我们下面将要揭露的任何问题,凝聚了最牛叉的大神们数年心血的精华,为什么不用它?撒泡尿照照你自己吧!)
[指导意见]:重用标准库算法,别再造些糟透了的轮子了。更快更简单更安全!!
    class Complex {
    
public:
        Complex( 
double real, double imaginary = 0 )
          : _real(real), _imaginary(imaginary) 
{};
1.风格:
由于第二个参数有默认值,所以可以用作一个单参数构造函数,所以会出现隐式的转换。而很可能本意不是这样的。(容易造成double被隐式转换成Complex)
[指导意见]:留神隐式转换。用显式(explicit)定义的构造函数就没问题了。
 void operator+ ( Complex other ) {
            _real 
= _real + other._real;
            _imaginary 
= _imaginary + other._imaginary;
        }
2.风格:
参数应该为const Complex & other,而a = a+b应为a+=b。
[规则]:传引用,别传值。
[指导意见]:算术运算时这样更好,用a op=b代替a = a op b。视情况定。(毕竟有的类不允许这个操作op=)

3.风格:
operator+不应该作成员函数。而在本题的实现之下,你只能写a=b+1,不能写a=1+b。(1不是class Complex)出于效率的考虑,你可能想提供operator+(Complex, int)和operator+(int, Complex)。
[规则]:遵守这几条建议足矣。一元操作符作成员。= [] () 和->必须作成员。+= -= /= *=等作成员。其他二元操作符都不作成员。(Lakos96: 143-144; 591-595; Murray93: 47-49)

4.错误:operator+不应该改变对象的值。应该返回一个临时对象,临时对象含有相加的和。注意这里的返回值类型应为const Complex(不是Complex),防止出现a+b=c的形式。
(事实上,这道题里的operator+更像是operator+=)

5.风格:如果你定义了运算符op,那你还应该定义op=。这里定义了operator+,那就应该同时定义operator+=。

void operator<<( ostream os ) {
            os 
<< "(" << _real << "," << _imaginary << ")";
        }
(注意:如果重载了<<,那同时也应该处理常用的格式符。)
6.错误:第3条讲了,operator<<不应该作成员,参数也应该是(ostream &, const Complex &)。而且,最好也不要把它作友元。还有,它应该调用一个公共的成员函数来做输出用。
Complex operator++() {
            
++_real;
            
return *this;
        }
7.错误:这个函数应该返回ostream &,而且应该以return os结束,这样可以实现链式表达式,就像cout<<a<<b。
8.风格:前缀++应该返回引用Complex &。更符合直觉。
Complex operator++int ) {
            Complex temp 
= *this;
            
++_real;
            
return temp;
        }
9.风格:后缀++应该返回const Complex,防止a++++,把别人搞晕。
10.[指导意见]:用前缀操作来实现后缀,更好。

private:
double _real, _imaginary;
};
11.风格:尽量不要用下划线开头来命名。是,我以前习惯这么干,连《设计模式》这种牛书里面都这么写。因为C++标准里很多地方用到了下划线开头的标识符,所以尽量避免这么写。(我已经不用下划线开头去命名了,我改用下划线结尾了。。。。。)

给个改过的代码:
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;
}
//返回类型,为什么输入int?

ostream
& print( ostream& os ) const {
return os << "(" << real_
<< "," << imaginary_ << ")";
}
//公共public,返回ostream &

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);
}
//友元,调用公共print


写完,睡觉。









posted on 2012-02-22 00:16 高兴 阅读(162) 评论(0)  编辑 收藏 引用 所属分类: GotW


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

<2012年2月>
2930311234
567891011
12131415161718
19202122232425
26272829123
45678910

统计

常用链接

留言簿

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜