Programming 版 (精华区)
作者:Hub Sutter
译者:plpliuly
/*此文是译者出于自娱翻译的GotW(Guru of the Week)系列文章第6篇,原文的版权是属
于Hub Sutter(著名的C++专家,"Exceptional C++"的作者)。此文的翻译没有征得原
作者的同意,只供学习讨论。——译者
*/
#6 正确使用const (1997年5月21日提出)
难度:6/10
尽可能的使用const,但是不要滥用.我们将讨论几处很明显的和几处并不明显的应该
或不应该用const地方.(译者:是不是太拗口了?没办法,我的翻译水平实在有限,各位看官
担待了:-))
问题:
const是写出更安全的代码的一个利器.而且它还可以帮助编译器进行优化.应该尽可
能的多用...但是什么才叫做"尽可能"呢?
不对下面程序的结构和其他风格问题作挑剔,因为它仅仅是用来做示例说明.请只在
合适的地方简单的加上或删除"const"(包括一些变量和相关的关键字).附加题是:哪些地
方将使程序由于const的错误使用而产生编译错误或不确定的(undefined)结果?
class Polygon {
public:
Polygon() : area_(-1) {}
void AddPoint( const Point pt ) {
InvalidateArea();
points_.push_back(pt);
}
Point GetPoint( const int i ) {
return points_[i];
}
int GetNumPoints() {
return points_.size();
}
double GetArea() {
if( area_ < 0 ) // if not yet calculated and cached
CalcArea(); // calculate now
return area_;
}
private:
void InvalidateArea() { area_ = -1; }
void CalcArea() {
area_ = 0;
vector<Point>::iterator i;
for( i = points_.begin(); i != points_.end(); ++i )
area_ += /* some work */;
}
vector<Point> points_;
double area_;
};
Polygon operator+( Polygon& lhs, Polygon& rhs ) {
Polygon ret = lhs;
int last = rhs.GetNumPoints();
for( int i = 0; i < last; ++i ) // concatenate
ret.AddPoint( rhs.GetPoint(i) );
return ret;
}
void f( const Polygon& poly ) {
const_cast<Polygon&>(poly).AddPoint( Point(0,0) );
}
void g( Polygon& const rPoly ) {
rPoly.AddPoint( Point(1,1) );
}
void h( Polygon* const pPoly ) {
pPoly->AddPoint( Point(2,2) );
}
int main() {
Polygon poly;
const Polygon cpoly;
f(poly);
f(cpoly);
g(poly);
h(&poly);
}
答案:
class Polygon {
public:
Polygon() : area_(-1) {}
void AddPoint( const Point pt ) {
InvalidateArea();
points_.push_back(pt);
}
1.既然point对象是值拷贝方式传递的,声明为const没有多大的意义.
Point GetPoint( const int i ) {
return points_[i];
}
2.同上面一样.const的传值参数通常是没什么用处的,而且只会容易引起误解.
3.此处应该是一个const的成员函数,因为它并没有改变对象的状态.
4.(有争议)对于非原生类型(non-builtin)的返回值拷贝通常应该是const的.这会有利于
调用该函数的代码通过编译器防止修改返回的临时对象(比如,"poly.GetPoint(i) = Po
int(2,2);"...如果真想这么做,GetPoint应该返回对象的引用,而不是通过传值方式返回
临时对象上.我们在后面将要看到,让GetPoint返回const类型或const引用类型是很有意
义的,因为这会在operator+()中对于const的Polygon对象的处理很有用)
注意:Lakos反对返回const类型,他认为这样做会妨碍模板的实例化.不过值得注意的是
:对于原生类型来讲,确实是没有必要返回const类型的(比如返回"const int").
[忠告]:对于非原生类型的传值返回,尽量返回一个const的类型值.
int GetNumPoints() {
return points_.size();
}
5.这个函数也应该是const.
(此处就不应该返回const int,因为int已经是一个右值,加上const会妨碍模板的实例化
,而且容易使人迷惑,产生误解)
double GetArea() {
if( area_ < 0 ) // if not yet calculated and cached
CalcArea(); // calculate now
return area_;
}
6.尽管这个函数改变了对象的内部状态,但它应该是const.因为对象的可观察的状态没有
改变(我们做的让area_保存计算结果只是一个实现细节,从逻辑上讲对象应该是const的
).这意味着area_应该声明为mutable,如果你的编译器不支持mutable,就将就对area_进
行const_cast来代替(建议当编译器支持mutable后去掉这个const_cast),而把函数改为
const函数.
private:
void InvalidateArea() { area_ = -1; }
7.此处是可争议的,但我还是建议把这个函数设为const,这样做只是为了一致性.(从语义
上讲,这个函数只会在非const的函数中调用,因为它的目的就是在对象的状态改变时使得
area_保存的面积值无效)
void CalcArea() {
area_ = 0;
vector<Point>::iterator i;
for( i = points_.begin(); i != points_.end(); ++i )
area_ += /* some work */;
}
8.这个成员函数肯定应该是const.毕竟,它会在另一个const的成员函数中调用,比如说G
etArea().
9.既然这个迭代器不应该改变上述points_collection的状态,因此应该是const_iterat
or.
vector<Point> points_;
double area_;
};
Polygon operator+( Polygon& lhs, Polygon& rhs ) {
10.当然应该是传递const引用作为参数.
11.返回类型也应该是const的.
Polygon ret = lhs;
int last = rhs.GetNumPoints();
12.既然"last"不需改变,那就声明为"const int"类型.
for( int i = 0; i < last; ++i ) // concatenate
ret.AddPoint( rhs.GetPoint(i) );
(GetPoint()应该声明为const成员函数,而且返回const类型或const引用类型的另一个原
因)
return ret;
}
void f( const Polygon& poly ) {
const_cast<Polygon&>(poly).AddPoint( Point(0,0) );
附件问题的答案:如果被引用的对象声明为const的(就如下面的f(cpoly)),这个结果
就是不确定的.这个参数不是一个真正的const,因此不要声明为const!
}
void g( Polygon& const rPoly ) {
rPoly.AddPoint( Point(1,1) );
}
13.此处的"const"是没有用处的,因为引用不可能被更改去引用另一个不同对象.[译者的
题外话:引用和被其引用的对象的关系是在引用初始化时确定的,此后不会发生变化.因此
引用变量也必须在声明的时候初始化,对于引用类型的类成员变量也必须通过构造函数的
初始化序列进行初始化.]
void h( Polygon* const pPoly ) {
pPoly->AddPoint( Point(2,2) );
}
14.这个"const"同样也是多余的,但是原因与上面不同:因为你传递的是一个指针的值,这
种写法和上面传递"const int"没有多大的区别.
(如果你认为附加题的答案是此处会引起编译器错误,那么,对不起,这是一个很合法的C+
+写法.你或许在想应该把"const"移到&或*的左边,但那会使函数体中发生编译错误[译者
:移到&或*左边的const是指引用或指针指向的对象应该是const的,上面代码中的const意
义是指引用或指针自身是const的])
int main() {
Polygon poly;
const Polygon cpoly;
f(poly);
此处没错.
f(cpoly);
此处如果f()试图将const属性cast掉,然后改变参数值,将会产生一个不确定的结果.
g(poly);
此处没问题.
h(&poly);
此处没问题.
}
好了,我们最后可以如下得到经过修改后的版本(记住,只是修改了const,不是所有的风格
问题都修改了):
class Polygon {
public:
Polygon() : area_(-1) {}
void AddPoint( Point pt ) { InvalidateArea();
points_.push_back(pt); }
const Point GetPoint( int i ) const { return points_[i]; }
int GetNumPoints() const { return points_.size(); }
double GetArea() const {
if( area_ < 0 ) // if not yet calculated and cached
CalcArea(); // calculate now
return area_;
}
private:
void InvalidateArea() const { area_ = -1; }
void CalcArea() const {
area_ = 0;
vector<Point>::const_iterator i;
for( i = points_.begin(); i != points_.end(); ++i )
area_ += /* some work */;
}
vector<Point> points_;
mutable double area_;
};
const Polygon operator+( const Polygon& lhs,
const Polygon& rhs ) {
Polygon ret = lhs;
const int last = rhs.GetNumPoints();
for( int i = 0; i < last; ++i ) // concatenate
ret.AddPoint( rhs.GetPoint(i) );
return ret;
}
void f( Polygon& poly ) {
poly.AddPoint( Point(0,0) );
}
void g( Polygon& rPoly ) { rPoly.AddPoint( Point(1,1) ); }
void h( Polygon* pPoly ) { pPoly->AddPoint( Point(2,2) ); }
int main() {
Polygon poly;
f(poly);
g(poly);
h(&poly);
}
--
(结束)
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:202.268毫秒