【Effective C++】不要重新定义继承而来的缺省参数值

本文探讨了C++中继承类重定义缺省参数值可能导致的非预期行为,因为缺省参数是静态绑定的,而virtual函数是动态绑定。静态类型决定了缺省参数的选择,而动态类型决定了virtual函数的调用。建议使用Non-Virtual Interface (NVI)模式来避免此类问题。
摘要由CSDN通过智能技术生成

现在考虑这样一种继承体系:

class Shape
{
public:
	enum ShapeColor {Red, Green, Blue};
	virtual void draw(ShapeColor color = Red) const = 0;
	//...
};

class Rectangle : public Shape
{
public:
	virtual void draw(ShapeColor color = Green) const;
};

class Circle  : public Shape
{
public:
	virtual void draw(ShapeColor color) const;
	//...
};

你希望缺省情况下,任何继承自 Shape 的类型了颜色为红色。但是同时呢,你在 Rectangle 类中对这个缺省的颜色进行了重定义,希望 Rectangle 的缺省颜色为绿色。然而事实真的如此吗?

考虑下面这种情况:

Shape *ps;
Shape *pc = new Circle();
Shape *pr = new Rectangle();

pc->draw();
pr->draw();

你会发现, pc->draw() 画出来的圆形是红色,pr->draw() 画出来的矩形,仍然是红色。这是因为:
virtual function 是动态绑定,而缺省参数值是静态绑定的。


静态绑定与动态绑定


对象的类型分为静态类型和动态类型。当然这里的对象只针对指针类型的对象,包括引用。

  • 静态类型:就是对象在程序中被声明的类型
  • 动态类型:对象目前所实际指向的类型

virtual 函数是动态绑定的,也就是说,当调用一个 virtual 函数时,究竟调用继承体系中哪一份代码,取决于发出调用的那个对象的动态类型;

缺省参数却是静态绑定的,也就是说,当调用一个函数缺省参数的函数时,调用的是发出调用的那个对象的静态类型。

因此,对于上面的 pr->draw(); 由于 draw() 是 virtual function,所以它调用的是发出调用对象 pr 的动态类型 Rectangle 中的 draw() 函数,即 Rectangle::draw();但是,对于缺省参数,由于 pr 的静态类型为 Shape,所以获得的是 Shape::draw() 中的缺省参数 Red。

所以,不应该对继承而来的缺省参数值进行重新定义,否则它可能会导致非预期行为。


NVI 实现 virtual function

对于上面这种情景,应该利用 NVI 去实现 virtual function,放置子类去重写缺省参数。关于 NVI,可以参考这篇文章

class Shape
{
public:
	enum ShapeColor {Red, Green, Blue};
	void draw(ShapeColor color = Red) const
	{
		doDraw(color);
	}
	//...
private:
	virtual void doDraw(ShaperColor color) const;
	//...
};

class Rectangle : public Shape
{
public:
	//...
private:
	virtual void doDraw(ShaperColor color) const;
};

class Circle  : public Shape
{
public:
	//...
private:
	virtual void doDraw(ShaperColor color) const;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值