Effective C++ (E3 41)笔记之了解隐式接口和编译器多态

对于class而言,接口是显式的,以函数签名式(函数名、参数类型、返回类型)为根基。多态则是通过virtual函数发生在运行期。

如有个类widget:

class Widget
{
public:
	Widget(std::string name="default", std::size_t size=0)
		:sname(name), ssize(size)
	{

	}
	Widget(std::size_t size=0)
		:sname("default"), ssize(size)
	{

	}
	virtual ~Widget(){

	}
	void print() const{
		cout<<"sname: "<sname, other.sname);
		std::swap(this->ssize, other.ssize);
	}
	bool operator!=(const Widget other) const{
		return (this->sname != other.sname);
	}
private:
	std::string sname;
	std::size_t ssize;
};


对于template而言,接口是隐式的,以有效表达式为根基。多态则是通过template实例化和函数重载解析发生于编译期。

如有个函数模板:

template
void doProcessing(T& w)
{
	if (w.size() > 10 && w != someNastyWidget) {
		T temp(w);
		temp.normalize();
		temp.swap(w);
	}
}


凡涉及w的任何函数调用,如operator>和operator!=,有可能造成template实例化使得调用成功。以不同的模板参数T实例化模板将得到并调用不同的函数,这就是编译期多态

w必须支持哪些接口?系于template中w身上的操作来决定。乍看w的类型T好像要支持size、normalize、swap成员函数、copy构造函数(建立temp)、不等比较操作符。

其实不尽然。

w提供的size()必须返回整数值吗?不一定。诚然w要提供size()(当然也能继承父类得到),但是注意operator被重载的情况,即operator>两旁既可以是数值类型,也可以是其他自定义类型:

bool operator>(const Mem& lhs, const Mem& rhs)
{
	return (lhs.mmem > rhs.mmem);
}

现在size()只要返回一个这么一个对象X,它和int 10能够调用重载版本的operator>。如下一种实现,对象X既可以是Mem;也可以是int。同样operator>右边的对象Y既可以Mem;也可以是int,只要Mem支持隐式转换:

struct Mem
{
	Mem(int num)
		:mmem(num){}
	int mmem;
};

验证:

#define someNastyWidget 128	
	Widget wg0("fffff",20);
	doProcessing(wg0);
	wg0.print();	
	Widget wg1("fffff",2);
	doProcessing(wg1);
	wg1.print();

结果(其中someNastyWidget 128被隐式转换为了Widget,用于调用成员函数operator!=):



w需要提供成员函数operator!=吗?不一定。同样如下一种实现,operator!=被重载,左操作数或右操作数是WidgetSp,或者能被隐式转换为WidgetSp:

bool operator!=(const WidgetSp& lhs, const WidgetSp& rhs){
	return true;
}

class WidgetSp
{
public:
	WidgetSp(std::string name="default", const Mem& Memobj=Mem(0))
		:sname(name), smem(Memobj)
	{

	}
	WidgetSp(const Mem& Memobj=Mem(0))
		:sname("default"), smem(Memobj)
	{

	}
	WidgetSp(int num)
		:sname("default"), smem(Mem(num))	//it only canbe convert once, int->Mem->WidgerSp is prohibit
	{

	}
	virtual ~WidgetSp(){

	}
	void print() const{
		cout<<"sname: "<sname, other.sname);
		std::swap(this->smem.mmem, other.smem.mmem);
	}

private:
	std::string sname;
	Mem smem;

};

验证:

	WidgetSp wgs0("shit",3);
	doProcessing(wgs0);
	wgs0.print();
	WidgetSp wgs1("shit",13);
	doProcessing(wgs1);
	wgs1.print();


结果(注意隐式转换只能被执行一次,从int->Mem->WidgerSp编译器是不认的,因此另外提供构造函数WidgetSp(int num)):



但无论“w.size() > 10 && w != someNastyWidget”涉及什么实际类型,导致什么行为,它都必须与bool兼容。当然除了上面讨论的size、operator!=以外,T必须支持normalize、swap、copy构造函数是没有疑问的。

总之,就像无法以“与class提供显式接口矛盾”的方式使用对象一样,也无法以“不支持template要求的隐式接口”在template中使用一样,两者都通不过编译。

你可能感兴趣的:(C++,算法)