如何写出优质的C++代码:(九)C++类模板的继承问题

C++类模板最早被使用在容器中,随着C++应用场景的不断扩展,C++类模板的作用得到了极大的丰富。

多态是C++一个重要的特性,我们一般将多态分为“编译期多态”和“运行期多态”,这两个对多态的划分,源自于对“哪一个重载函数该被调用(编译期)”和“哪一个virtual函数将被绑定(运行期)”这两个问题的思考。对于类模板,由于模板参数的不确定性,引出了很多编译器无法知晓的参数类型,看看下面这段代码:

template<class T>
class CBufferBase
{
public:
	CBufferBase(void){}
	virtual ~CBufferBase(void){}
	typedef std::list<T*>		PackList;
	typedef PackList::iterator	PackIterator;  //错误
......
};

以上代码将无法编译通过。正如我们所知,T是一个不确定的模板参数,这导致了PackList也是一个不确定的对象(或者参数等等,尽管std::list是我们熟知的容器),最终导致了PackList::iterator对于编译器来说是一个未知的对象,然后通过这个位置的对象去声明一个实例,肯定不符合C++的语法规则。为了解决上述问题,我们必须告诉编译器:PackList::iterator是一个类型。对上述代码做如下修改:

template<class T>
class CBufferBase
{
......
	typedef typename PackList::iterator	PackIterator;  
......
};

通过紧邻PackList::iterator之前加上typename告诉编译器它是一个类型。

看看下面这个场景:一个汽车销售代理商目前有若干燃油车和电动车待销售,客户来店看车的时候,工作人员需要调出客户感兴趣车型的信息,其中有一些信息,燃油车和电动车的信息类型不一样,比如:燃油车的油箱容量和电动车的电池容量,调出信息之后将所有信息打印出来,看看实现代码:

//燃油车
class FuelVehicle
{
    ......
public:
	void GetCapacity() {};	//获取油箱容量

};
//电动车
class ElecVehicle
{
    ......
public:
	void GetCapacity() {};	//获取电池容量

private:

};
//打印信息
template<typename Vehicle>
class PrintVehicleInfo
{
    ......
public:
	void PrintInfo()
	{
		Vehicle V;
		V.GetCapacity();    //获取容量
		//......
	}
};

上面的代码看起来一切正常。假设此时我们需要将每次打印的信息保存到电脑数据库,我们从上述代码中派生一个类,可以轻松解决这个需求:

template<typename Vehicle>
class SaveVehicleInfo : public PrintVehicleInfo<Vehicle>
{
......
public:
    //打印并保存汽车信息
	void PrintAndSaveInfo()
	{
		PrintInfo();//错误
		......
	}
};

这个派生类中定义了一个函数PrintAndSaveInfo(),在基类中没有同名称的函数,这是一个好的设计。上述这段代码将无法编译通过,原因是编译器找不到函数PrintInfo()的定义,虽然我们用眼睛可以看到PrintInfo()就在基类中,但是编译器就是看不到,这是上面原因呢?

问题是这样的,对于派生类SaveVehicleInfo ,编译器并不知道它继承哪个class。也许你会说是PrintVehicleInfo,但是PrintVehicleInfo中的Vehicle是一个模板参数,在SaveVehicleInfo被具现化之前,编译器不知道Vehicle具体是什么,从而也就不知道PrintVehicleInfo<Vehicle>具体是什么,因而无法确定它是否有函数PrintInfo(),最终编译失败。

我们必须让编译器避免观察“被模板化的基类”。解决的办法主要有两个,一是在调用的基类函数前面加上this->:

    ......
	void PrintAndSaveInfo()
	{
		this->PrintInfo();
		......
	}

二是使用using声明式:

    ......
    using PrintVehicleInfo::PrintInfo;
	void PrintAndSaveInfo()
	{
		PrintInfo();
		......
	}

对于调用基类成员函数无效的问题,编译器的诊断时间发生在早期(也就是在解析派生类模板的时候),也有可能是在晚期(当模板参数被具体的实参具现化的时候),C++的思想是尽早诊断。出现此类问题,我们应告之编译器的具体调用路径。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值