【问题】
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)
CalcArea(); //如果还没被计算和保存,那么现在开始
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)
{
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);
}
【解答】
void AddPoint(const Point pt)
{
InvalidateArea();
points_.push_back(pt);
}
1.因为Point对象采用传值方式,声明为const没有什么用处,不会对效能显著影响。
Point GetPoint(const int i)
{
return points_[i];
}
2.同1一样,const传值没意义,反而容易引起误解。
3.这个成语函数应该定义成const成员函数,因为它不改变对象状态。
4.如果该函数返回的类型不是一个内置类型的话,通常应该将其返回类型也声明为const,这样有利于调用者不能企图修改返回的临时变量。
【原则】:在函数内对于非内置类型采用值返回的方法,最好让函数返回一个const值。
int GetNumPoints()
{
return points_.size();
}
5.函数本身应该是const。
double GetArea()
{
if(area_ < 0)
CalcArea(); //如果还没被计算和保存,那么现在开始
return area_;
}
6.尽管这个函数对象的内部状态改变了,但它还是要应该被声明为const,因为被修改对象的可见状态没有发生变化。这意味着area_应该被声明为mutable。
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.iterator不会改变point_的状态,这应该声明为一个const_iterator。
Polygon operator+ (Polygon& lhs,Polygon& rhs)
10.这里应该参数前加const。
11.返回值应该是const,防止修改临时变量。
Polygon ret = lhs;
int last = rhs.GetNumPoints();
12.last不会改变,应该声明为const。
void f(const Polygon& poly)
{
const_cast<Polygon&>(poly).AddPoint(Point(0,0));
}
如果引用的对象被声明为const,那么这里的结果将是未定义的。其参数事实上不是真的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参数一样毫无意义。
f(cpoly);
这里,f()企图放弃const属性从而修改器参数的时候,会产生不确定的结果。
正确的代码如下:
class Polygon
{
public:
Polygon():area_(-1){}
void AddPoint(Point pt)
{
InvalidateArea();
points_.push_back(pt);
}
Point GetPoint(int i) const
{
return points_[i];
}
const int GetNumPoints() const
{
return points_.size();
}
double GetArea() const
{
if(area_ < 0)
CalcArea(); //如果还没被计算和保存,那么现在开始
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_;
};
Polygon operator+ (Polygon& lhs,Polygon& rhs)
{
Polygon ret = lhs;
const int last = rhs.GetNumPoints();
for(int i = 0; i < last; ++i)
{
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);
}