您的位置:首页 > 编程语言 > C语言/C++

Effective C++:条款43:学习处理模板化基类内的名称

2014-09-20 13:56 405 查看
(一)

注意从 “面向对象的C++” 转向 “模板C++” 时继承可能遭遇问题 :由于基类模板可能被特化,而该特化版本可能会改变成员,因此C++拒绝在模板化基类中寻找继承而来的名称。

(二)

看下面的例子:

假设将信息传送到不同的公司去,传送方式包括明文传送和密文传送,采用模板类的设计方法:

class CompanyA {
public:
...
void sendClearText(const string& msg);
void sendEncrypted(const string& msg);
...
};

class CompanyB {
public:
...
void sendClearText(const string& msg);
void sendEncrypted(const string& msg);
};

...//其他公司的classes

class MsgInfo {...}; //这个class为将来生产保存信息

template<typename Company>
class MsgSender {
public:
...
void sendClear(const MsgInfo& info) {
string msg;
...//根据info产生信息
Company c;
c.sendClearText(msg);
}
void sendSecret(const MsgInfo& info) {...}//这里调用的是c.sendEncrypted
};
如果想要添加发送信息时记录信息的功能,采用继承的方式:

template<typename Company>
class LoggingMsgSender : public MsgSender<company> {
public:
...
void sendClearMsg(const MsgInfo& info) {
...//将传送前的信息写至记录信息
sendClear(info);//试图调用基类成员函数完成发送,但是不能通过编译。这是因为在派生类没有具体化时,不知道Company是什么类型,也就不知道基类中是否存在该函数
...//将传送后的信息写至记录信息
}
...
};
编译器不让其通过编译.此时的编译器抛出了"sendClear不存在"的抱怨.可sendClear明显就在base
class内部啊?郁闷,这是为什么倪?别急,我来解释一下:当编译器遇到LoggingMsgSender定义式时,由于Company是一个template参数,不到LoggingMsgSender被具现化的时候,编译器是无法知道Company是什么的.由于无法知道Company是什么,那么它就无法确定MsgSender是什么,也就无法获知其否有个sendClear函数. 停一下,你可能现在对我的解释会有一个问题:为什么根据template Company就不能确定Company是什么了呢?template Company不是有声明式嘛!在这里我提出:我们不能根据一般性模板类的声明式来确定具现化类具有的操作,因为模板类有个特化声明版本的问题.

(三)

改进方法:

(1)在基类函数调用动作之前加上this指针:

template <typename Company>
void LoggingMsgSender<Company>::sendClearMsg(const MsgInfo& info){
...
this->sendClear(info); //ok
...
}


(2)使用using声明式在类内使用基类命名空间:

template <typename Company>
class LoggingMsgSender:public MsgSender<Company>{
public:
//这里的情况不是base class名称被derived class名称遮掩,而是编译器不进入base base
//作用域查找,于是我们通过using声明式告诉它,请它这么做
using MsgSender<Company>::sendClear;//告诉编译器,请它假设sendClear位于base class内
...
void sendClearMsg(const MsgInfo& info){
...
sendClear(info);//ok
...
}
};


(3)明确限定函数是基类中的函数:

template <typename Company>
class LoggingMsgSender:public MsgSender<Company>{
public:
...
void sendClearMsg(const MsgInfo& info){
...
MsgSender<Company>::sendClear(info); //ok
...
}
...
};
不过此方法有一个很明显的暇疵:如果被调用的是virtual函数,上述的明确资格修饰会关闭"virtual绑定行为".

所以还是尽量选择前两种方法吧。

请记住:

(1)可在derived class template内通过“this->”指涉base class template内的成员名称,或藉由一个明白写出的“base class资格修饰符”完成。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: