Effective C++之条款37、38

条款37:绝不重新定义继承而来的缺省参数值

    我们只能继承两种函数:virtual和non-virtual函数。然而重新定义一个继承而来的non-virtual函数永远是错误的(条款36),所以本条款的讨论局限于“继承一个带有缺省参数值的virtual函数”。

    virtual函数系动态绑定,而缺省参数值却是静态绑定。对象的所谓静态类型,就是它在程序中被声明时所采用的类型。考虑以下的class继承体系:

class Shape {
public:
	enum ShapeColor {Red, Greeen, Blue};
	virtual void draw(ShapeColor color = Red) const = 0;
	...
};
class Rectangle: public Shape {
public:
	//注意,赋予不同的缺省参数值,这很糟糕。
	virtual void draw(ShapeColor color = Greeen) const;
	...
};
class Circle: public Shape {
public:
	virtual void draw(ShapeColor color) const;
	//请注意,以上这么写则当客户以对象调用此函数,一定要指定参数值。
	//因为静态绑定下这个函数并不从其基类继承缺省参数值。
	//但若以指针(或引用)调用此函数,可以不指定参数值,
	//因为动态绑定下这个函数会从基类继承缺省参数值
	...
};

    现在考虑这些指针:

Shape* ps;               //静态类型为Shape*
Shape* pc = new Circle;  //静态类型为Shape*
Shape* pr = new Rectangle;

    对象的所谓动态类型则是指“目前所指对象的类型”。也就是说,动态类型可以表现出一个对象将会有什么行为。以上例而言,PC的动态类型时Circle*,pr的动态类型是Rectangle*。ps没有动态类型,因为它尚未指向任何对象。

    动态类型可在程序执行过程中改变(通常由赋值动作完成):

ps = pc;   //ps的动态类型如今是Circle*
ps = pr;   //ps的动态类型如今是Rectangle*

    Virtual函数系动态绑定而来,意思是调用一个virtual函数时,究竟调用哪一份函数实现代码,取决于发出调用的那个对象的动态类型:

pc->draw(Shape::Red);   //调用Circle::draw(Shape::Red)
pr->draw(Shape::Red);   //调用Rectangle::draw(Shape::Red)

    virtual函数是动态绑定的,而缺省参数值却是静态绑定。意思是你可能会在调用一个定义于派生类的virtual函数的同时,却使用基类为它所指定的缺省参数值:

pr->draw();     //调用Rectangle::draw(Shape::Red)

    此例中,pr的动态类型是Rectangle*,所以调用的是Rectangel的virtual函数,但是由于pr的静态类型是Shape*,所以此调用的缺省参数值来自基类而非派生类。

    为什么C++坚持以这种方式来运作呢?答案在于运行期效率。如果缺省参数值是动态绑定,编译器就必须有某种办法在运行期为virtual函数决定适当的参数缺省值。这比目前实行的“在编译期间决定”的机制更慢而且更复杂,为了程序的执行速度和编译器实现上的简易度,C++做了这样的取舍。

    如果我们遵守这条规则,并同时提供相同的缺省参数值给基类和派生类:

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

    这将造成的是代码重复,以及为今后更改缺省值埋下隐患,因为你必须同时改变基类和派生类该函数的缺省参数值。可替代的NVI手法如下:

扫描二维码关注公众号,回复: 10163483 查看本文章
class Shape {
public:
	void draw(ShapeColor color = red) const  //如今是一个non-virtual
	{
		doDraw(color);                      //调用一个virtual
	}
private:
	virtual void doDraw(ShapeColor color) const = 0; //真正的工作在此完成
};
class Rectangle: Shape {
public:
	...
private:
	virtual void doDraw(ShapeColor color) const; //不须指定缺省参数值
};

条款38:通过复合塑膜出has-a或“根据某物实现出”

    复合是类型之间的一种关系,当某种类型的对象内含其他类型的对象,便是这种关系。例如:

class Address {...};                //某人的住址
class PhoneNumber {...};
class Person {
public:
	...
private:
	string name;                   //合成成分物
	Address address;               //同上
	PhoneNumber voiceNumber;       //同上
	PhoneNumber faxNumber;         //同上
};

请记住

  • 复合的意义和public继承完全不同。
  • 在应用域,复合意味着has-a。在现实域,复合意味着is-implemented-in-terms-of.
发布了33 篇原创文章 · 获赞 6 · 访问量 562

猜你喜欢

转载自blog.csdn.net/weixin_43519984/article/details/102942244