C++之 模板化基类 的名称处理
2014-05-03 10:59
387 查看
在引入模板之后,我们面临一个新的问题,那就是如果继承自一个模板基类,是否跟继承一般的类有什么区别呢?
就是说,如果一个类继承自某个模板类,那么该类应该注意一些什么呢?其实,它与一般类的继承是很不一样的。
先举个简单的例子:
我们可以看到,在上述代码中,Derived类中的成员函数调用了Base类的成员函数,显然这是很合理的,因为PrintBase并没有被声明为virtual,因此,派生类直接继承其声明与实现,可是编译器却不这么认为,编译器给出的错误是:
error: there are no arguments to 'printBase' that depend on a template parameter, so a declaration of 'printBase' must be available。
这里的编译器并不承认PrintBase的存在,并且认为该函数它不认识,这与面向对象的C++出现了分歧。
关于此分歧的原因,可以由下面的例子来解释,归根结底,都是模板基类的引入。
显然,在上述的LogSendMsg中,该类只有在模板形参Company被 实例化 之后才知道实际调用的是哪个Company。显然在上述的代码中,CampanyC是没有定义SendClear函数的,编译器不可能一一的去预先知道这样的情况,因为用户指不定就为某家公司定义了一个特化版本,会直接导致该函数失效。
因此,编译器本着 “尽早报告错误”的可能性,在看到SendClear函数的时候,编译器不会去寻找该函数的定义,只当该函数没声明。结果就如上面的错误了。
注:这里就看出了模板基类与一般的基类的区别,一般的基类会沿着类的作用域一层一层的往上找,知道找到该函数的声明。但是模板类不会干这些无用功,是因为它知道在后续自己被 “实例化”之后,该函数可能根本就不存在(例如上述CompanyC的特化版本)。
因此,对于编译器的这种 “不进入模板基类去寻找定义” 的行为,必须由用户告诉编译器该怎么干:
1 可以使用this指针,即 this->sendClear 函数。
2
使用using声明,显式地告诉编译器,我要用的是基类的sendClear函数。
3 使用作用域符号,告诉编译器这个函数是哪个类的。
值得注意的是,上述三种途径,是针对该函数一定在基类中有定义才这么干,例如上述的CompanyC并没有定义sendClear函数,那么上述做法是失效的。
总之,在使用模板基类时,对于基类中定义的名字,在派生类中必须显式地指明,因为在基类实例化之前,编译器对于基类的定义一无所知。。
就是说,如果一个类继承自某个模板类,那么该类应该注意一些什么呢?其实,它与一般类的继承是很不一样的。
先举个简单的例子:
template class Base { public: void printBase() {cout << "call Base printBase() ..." << endl;} }; template class Derived : public Base { public: void printDerived() { cout << "call Derived printDerived() ... " << endl; printBase(); } };
我们可以看到,在上述代码中,Derived类中的成员函数调用了Base类的成员函数,显然这是很合理的,因为PrintBase并没有被声明为virtual,因此,派生类直接继承其声明与实现,可是编译器却不这么认为,编译器给出的错误是:
error: there are no arguments to 'printBase' that depend on a template parameter, so a declaration of 'printBase' must be available。
这里的编译器并不承认PrintBase的存在,并且认为该函数它不认识,这与面向对象的C++出现了分歧。
关于此分歧的原因,可以由下面的例子来解释,归根结底,都是模板基类的引入。
class CompanyA { public: void sendClearMsg() { cout << "call company A sendClear " << endl;} void sendEncryptedMsg() { cout << "call company A sendEncryptedMsg " << endl;} }; class CompanyB { public: void sendClearMsg() { cout << "call company B sendClear " << endl;} void sendEncryptedMsg() { cout << "call company B sendEncryptedMsg " << endl;} }; //首先定义了两个类,用于表示各个公司自己传送信息的方式。 //CompanyC与上述两个公司不一样,只允许将数据加密之后再传送 //companyC only send encrypted Msg class CompanyC { public: void sendEncryptedMsg() { cout << "call company C sendEncryptedMsg " << endl;} }; //定义了一个传送信息的类,对于不同的公司,封装了公司的传送信息的方式,取决于模板实参。 template class MsgSender { public: void sendClear() { cout << "call MsgSender sendClear ... " << endl; Company c; c.sendClearMsg(); } void sendEncrypted() { cout << "call MsgSender sendEncrypted ... " << endl; Company c; c.sendEncryptedMsg(); } }; //针对CompanyC的特化版本的传送信息方式,即如果模板实参为CompanyC,那么直接调用此模板类 template<> class MsgSender { public: void sendClear() { cout << "call MsgSender sendClear ... " << endl; } void sendEncrypted() { cout << "call MsgSender sendEncrypted ... " << endl; } }; //在每次将信息发出去时,记录下发出的信息。 //log the Message template class LogSendMsg : public MsgSender { public: void Log() { cout << "call LogSendMsg log ..." << endl; // sendClear(); //错误!!因为LogSendMsg类不知道这个sendClear函数是什么?甚至不知道是哪儿来的。 //error: there are no arguments to 'sendClear' that depend on a template parameter, so a declaration of 'sendClear' must be available| cout << "call LogSendMsg log end... " << endl; } };
显然,在上述的LogSendMsg中,该类只有在模板形参Company被 实例化 之后才知道实际调用的是哪个Company。显然在上述的代码中,CampanyC是没有定义SendClear函数的,编译器不可能一一的去预先知道这样的情况,因为用户指不定就为某家公司定义了一个特化版本,会直接导致该函数失效。
因此,编译器本着 “尽早报告错误”的可能性,在看到SendClear函数的时候,编译器不会去寻找该函数的定义,只当该函数没声明。结果就如上面的错误了。
注:这里就看出了模板基类与一般的基类的区别,一般的基类会沿着类的作用域一层一层的往上找,知道找到该函数的声明。但是模板类不会干这些无用功,是因为它知道在后续自己被 “实例化”之后,该函数可能根本就不存在(例如上述CompanyC的特化版本)。
因此,对于编译器的这种 “不进入模板基类去寻找定义” 的行为,必须由用户告诉编译器该怎么干:
1 可以使用this指针,即 this->sendClear 函数。
2
使用using声明,显式地告诉编译器,我要用的是基类的sendClear函数。
3 使用作用域符号,告诉编译器这个函数是哪个类的。
值得注意的是,上述三种途径,是针对该函数一定在基类中有定义才这么干,例如上述的CompanyC并没有定义sendClear函数,那么上述做法是失效的。
总之,在使用模板基类时,对于基类中定义的名字,在派生类中必须显式地指明,因为在基类实例化之前,编译器对于基类的定义一无所知。。
相关文章推荐
- Effective C++ 43条 处理模板化基类内的名称
- Effective C++ Item 43 学习处理模板化基类内的名称
- 读书笔记_Effective_C++_条款四十三:学习处理模板化基类的名称
- Effective C++ Item 43 学习处理模板化基类内的名称
- 条款43:学习处理模板化基类的名称
- 《Effective C++》:条款43:学习处理模板化基类内的名称
- 条款43:学习处理模板化基类内的名称
- Effective C++学习笔记_条款43:学习处理模板化基类内的名称
- Effective C++笔记_条款43 学习处理模板化基类内的名称
- Item 43: 学习处理模板化基类内的名称
- Effective C++:条款43:学习处理模板化基类内的名称
- 《Effective C++》读书笔记之item43:学习处理模板化基类内的名称
- 学习处理模板化基类内的名称
- 条款43:学习处理模板化基类内的名称
- 条款43:学习处理模板化基类内的名称
- Effective C++ 条款43 学习处理模板化基类内的名称
- effective C++ 条款 43:学习处理模板化基类内的名称
- 条款43:学习处理模板化基类内的名称
- Effective C++ (E3 43)笔记之学会处理模板化基类内的名称
- Effective C++笔记_条款43 学习处理模板化基类内的名称