假設我們要寫一個應用程序,它可以把消息傳送到幾個不同的公司去。消息既可以以加密方式也可以以明文(不加密)的方式傳送。如果我們有足夠的信息在編譯期間確定哪個消息將要發送給哪個公司,我們就可以用一個 template-based(模板基)來解決問題: class CompanyA {
public:
...
void sendCleartext(const std::string& msg);
void sendEncrypted(const std::string& msg);
...
};
class CompanyB {
public:
...
void sendCleartext(const std::string& msg);
void sendEncrypted(const std::string& msg);
...
};
... // classes for other companies
class MsgInfo { ... }; // class for holding information
// used to create a message
template
class MsgSender {
public:
... // ctors, dtor, etc.
void sendClear(const MsgInfo& info)
{
std::string msg;
create msg from info;
Company c;
c.sendCleartext(msg);
}
void sendSecret(const MsgInfo& info) // similar to sendClear, except
{ ... } // calls c.sendEncrypted
};
這個能夠很好地工作,但是假設我們有時需要在每次發送消息的時候把一些信息記錄到日志中。通過一個 derived class(派生類)可以很簡單地增加這個功能,下面這個似乎是一個合理的方法:template
class LoggingMsgSender: public MsgSender
public:
... // ctors, dtor, etc.
void sendClearMsg(const MsgInfo& info)
{
write "before sending" info to the log;
sendClear(info); // call base class function;
// this code will not compile!
write "after sending" info to the log;
}
...
};
注意 derived class(派生類)中的 message-sending function(消息發送函數)的名字 (sendClearMsg) 與它的 base class(基類)中的那個(在那裡,它被稱為 sendClear)不同。這是一個好的設計,因為它避開了 hiding inherited names(隱藏繼承來的名字)的問題(參見《C++箴言:避免覆蓋通過繼承得到的名字》)和重定義一個 inherited non-virtual function(繼承來的非虛擬函數)的與生俱來的問題(參見《C++箴言:絕不重定義繼承的非虛擬函數》)。但是上面的代碼不能通過編譯,至少在符合標准的編譯器上不能。這樣的編譯器會抱怨 sendClear 不存在。我們可以看見 sendClear 就在 base class(基類)中,但編譯器不會到那裡去尋找它。我們有必要理解這是為什麼。
問題在於當編譯器遇到 class template(類模板)LoggingMsgSender 的 definition(定義)時,它們不知道它從哪個 class(類)繼承。當然,它是 MsgSender
為了使問題具體化,假設我們有一個要求加密通訊的 class(類)CompanyZ:
class CompanyZ { // this class offers no
public: // sendCleartext function
...
void sendEncrypted(const std::string& msg);
...
};
template<>// a total specialization of
class MsgSender{ // MsgSender; the same as the
public: // general template, except
... // sendCleartext is omitted
void sendSecret(const MsgInfo& info)
{ ... }
};
template
class LoggingMsgSender: public MsgSender{
public:
...
void sendClearMsg(const MsgInfo& info)
{
write "before sending" info to the log;
sendClear(info); // if Company == CompanyZ,
// this function doesnt exist!
write "after sending" info to the log;
}
...
};
template
class LoggingMsgSender: public MsgSender{
public:
...
void sendClearMsg(const MsgInfo& info)
{
write "before sending" info to the log;
this->sendClear(info); // okay, assumes that
// sendClear will be inherited
write "after sending" info to the log;
}
...
};
template
class LoggingMsgSender: public MsgSender{
public:
using MsgSender::sendClear; // tell compilers to assume
... // that sendClear is in the
// base class
void sendClearMsg(const MsgInfo& info)
{
...
sendClear(info); // okay, assumes that
... // sendClear will be inherited
}
...
};
template
class LoggingMsgSender: public MsgSender{
public:
...
void sendClearMsg(const MsgInfo& info)
{
...
MsgSender::sendClear(info); // okay, assumes that
... // sendClear will be
} // inherited
...
};
LoggingMsgSenderzMsgSender;
MsgInfo msgData;
... // put info in msgData
zMsgSender.sendClearMsg(msgData); // error! wont compile