Effective C++ (E3 37)笔记之不重新指定继承而来的默认参数值

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/brahmsjiang/article/details/79177257

有个抽象类表示形状,其中纯虚函数draw有默认形参:

class shape{
public:
	enum shapecolor{Red,Green,Blue};
	virtual void draw(shapecolor col=Red) const=0;
	std::string retcol(shapecolor col) const{
		std::string str;
		switch(col)
		{
			case Red:
				str="Red";break;
			case Green:
				str="Green";break;
			case Blue:
				str="Blue";break;
			default:
				break;
		}
		return str;
	}
};
同时有两个继承类表示长方形和圆形,分别提供了带默认形参和不带默认形参的draw实现:

class rectangle:public shape{
public:
	virtual void draw(shapecolor col=Green) const{
		cout<<"rectangle::draw "<<retcol(col)<<endl;
	}
};

class circle:public shape{
public:
	virtual void draw(shapecolor col) const{	//when user call draw,para have to be set
		cout<<"circle::draw "<<retcol(col)<<endl;
	}
};
实例化对象后调用draw函数:

	rectangle rec;
	rec.draw();
	circle cir;
	cir.draw(shape::Blue);	//if no para,report err

	shape* ptr=new rectangle();
	ptr->draw();
	shape* ptr1=new circle();
	ptr1->draw();

结果为:


通过直接定义rectangle和circle的对象调用draw(),获取到的默认参数前者是Green而后者为空,必须指定否则报错。说明静态绑定下,默认参数即为rectangle和circle声明时的样子,无法从base类继承

而通过base指针调用rectangle和circle对象的draw(),获取到的默认参数却都是Red。这说明默认参数是静态绑定的,由于指针是base类型,故编译期就已决定是base的默认参数,尽管调用哪个virtual函数时动态绑定的。于是导致了base类和derived类各出一半力的怪异结果。

由此可见绝不应该重新定义继承而来的参数值。那么derived类该如何指定默认形参呢?

也指定为Red?那会导致代码重复而且会对base类产生依赖。即base类一旦改变参数值derived也得跟着改。

较好方法是使用NVI手段将draw函数移到private,并提供一个public non-virtual 函数(外覆器)调用真正干活的函数dodraw:

class shape{
public:
	enum shapecolor{Red,Green,Blue};
	void draw(shapecolor col=Red) const{
		dodraw(col);
	}
	std::string retcol(shapecolor col) const{
		std::string str;
		switch(col)
		{
			case Red:
				str="Red";break;
			case Green:
				str="Green";break;
			case Blue:
				str="Blue";break;
			default:
				break;
		}
		return str;
	}
private:
	virtual void dodraw(shapecolor col) const=0;
};

class rectangle:public shape{
private:
	virtual void dodraw(shapecolor col) const{
		cout<<"rectangle::draw "<<retcol(col)<<endl;
	}
};

class circle:public shape{
private:
	virtual void dodraw(shapecolor col) const{
		cout<<"circle::draw "<<retcol(col)<<endl;
	}
};
可见通过NVI手法仅在外覆器指定默认参数,因为non-virtual本来就不该被重新定义,否则属于名称覆盖和多态没关系了。当然derived类也就不指定默认参数了。现在调用完全符合预期:

	rectangle rec;
	rec.draw();

	circle cir;
	shape* ptr=○
	ptr->draw();
	ptr->draw(shape::Green);

展开阅读全文

没有更多推荐了,返回首页