happia
GotW #6: const的正确用法
原文在
http://www.gotw.ca/gotw/006.htm
看过Exceptional C++的可以跳过。
难度:6/10
尽量使用const,但别太过头了。这儿有个使用const的例子,有些是明显或不明显应该用const的,有些则是不该用const的。
问题:
const能帮你写出更安全的代码,也能帮助编译器优化。所以你应该尽量的使用const。但是尽量是多少呢?
别改变下面这段代码的结构,它是纯粹拼凑出来的。只能增加或删去const和一些变量。
加分题:找出代码中由于const的错误使用从而导致未定义的运行结果或者编译出错的地方。
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;
cons
t
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.因为pt是值传递的,所以const没什么用。
Point GetPoint(
const
int
i )
{
return
points_[i];
}
2.同上。通常值传递时const是没用的。
3.应该是const成员函数。因为GetPoint()没改变对象的状态。
4.(存争议)返回非内部类型的值时,通常应为const。这样当调用者试图修改这个临时对象的值时,编译器会报错。比如,poly.GetPoint(i)=Point(2,2);毕竟要让它运行,GetPoint()应该返回一个引用,而不是值。过一会还将看到GetPoint()返回常量或者const引用是有用处的,因为operator+()里它将要被用作const对象。
[备忘]:Lakos(pg.618)反对返回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_应该声明为mutable。如果你的编译器不支持mutable,那你就需要用const_cast处理area_。(最好在这里加上注释,下次编译器支持mutable后,及时改成mutable),但别忘了必须把函数声明成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,毕竟它会被GetArea()这样的const成员函数调用。
9.因为iterator不应该会修改points_ 向量的状态,所以应用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放在&或*的左边,那倒是会编译错误的。)
int
main()
{
Polygon poly;
const
Polygon cpoly;
f(poly);
没问题。
f(cpoly);
这里就会出现未定义的结果。f()会去掉cpoly的const限制,然后修改它。
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);
}
搞定。中间浏览器挂了一次,结果损失了2个小时的活儿。。现在学乖了,写几段就保存一次。
posted on 2012-02-22 17:27
高兴
阅读(223)
评论(0)
编辑
收藏
引用
所属分类:
GotW
只有注册用户
登录
后才能发表评论。
【推荐】100%开源!大型工业跨平台软件C++源码提供,建模,组态!
相关文章:
GotW #9: 内存管理 上部
GotW #8:竞态:异常安全
GotW #7:编译时依赖
GotW #6: const的正确用法
GotW #5: 覆盖虚函数
GotW #4 类技术
Gotw#3 使用标准库,或者重访问临时对象
GotW #2 临时对象
GotW #1 变量初始化
网站导航:
博客园
IT新闻
BlogJava
博问
Chat2DB
管理
导航
C++博客
首页
新随笔
联系
聚合
管理
<
2012年2月
>
日
一
二
三
四
五
六
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1
2
3
4
5
6
7
8
9
10
统计
随笔 - 10
文章 - 0
评论 - 0
引用 - 0
常用链接
我的随笔
我的评论
我参与的随笔
留言簿
给我留言
查看公开留言
查看私人留言
随笔分类
GotW(9)
(rss)
随笔档案
2012年2月 (10)
搜索
最新评论
阅读排行榜
1. 旧文新译:Herb Sutter纪念Dennis Ritchie(435)
2. GotW #7:编译时依赖(408)
3. GotW #1 变量初始化(297)
4. GotW #9: 内存管理 上部(263)
5. GotW #5: 覆盖虚函数(234)
评论排行榜
1. GotW #1 变量初始化(0)
2. GotW #2 临时对象(0)
3. Gotw#3 使用标准库,或者重访问临时对象(0)
4. GotW #4 类技术(0)
5. GotW #5: 覆盖虚函数(0)