条款43:GotW#6 正确使用const(Const-Correctness)

本文详细解释了如何在C++中正确使用const关键字,通过一个Polygon类的具体实例,展示了const在成员函数、参数及返回值中的应用技巧,并纠正了一些常见错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【问题】

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值