分享
 
 
 

Guru of the Week #6:正确使用const

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

作者: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) = Point(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的成员函数中调用,比如说GetArea().

9.既然这个迭代器不应该改变上述points_collection的状态,因此应该是const_iterator.

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);

}

--

(结束)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
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- 王朝網路 版權所有