2016-05-17 70 views
1

我試圖實現具有3個級別的信息的記錄器:一般(日期/時間),上下文,消息C++圖案:1x基站類+ NX派生類但具有_Last resort_派生類

爲了達到這個目標我想implemnet以下模式:

  1. Logger類(非此相關)
  2. Context類
    • 基類LoggerContext,這有根的功能erating一般水平的相關信息
    • 派生類,添加上下文中的具體的相關信息(具體爲應用程序的一部分)

有趣的部分開始,我嘗試有一個沒有上下文。也就是說,如果記錄器沒有上下文被調用,那麼應該使用單例LoggerContextNone

這裏我的代碼,不管我怎麼打開它,不編譯:

#include <string> 
#include <iostream> 
#include <stdexcept> 

using namespace std; 

enum class LoggerArea { 
     LOGGER_NONE, LOGGER_DOWNLOAD, LOGGER_COMPUTE, 
}; 

class ELoggerContext: std::runtime_error { 
     using std::runtime_error::runtime_error; 
}; 

class LoggerContextNone; // forward declaration, only needed for 
         // the commented version of the code 

class LoggerContext { 
protected: 
     LoggerArea mLA; 
public: 
     LoggerContext(LoggerArea la); 
     virtual ~LoggerContext() = 0; 
     /* 
     static LoggerContext& getEmptyContext() { 
       static LoggerContextNone loggerContextNone = { LoggerArea::LOGGER_NONE }; 
       return loggerContextNone; 
     } 
     */ 
     std::string getGeneral(); 
     virtual std::string getContext() = 0; // pure virtual 
}; 

string LoggerContext::getGeneral() { 
     return "general informations"; 
} 
LoggerContext::LoggerContext(LoggerArea la) : 
       mLA(la) { 
    if (la == LoggerArea::LOGGER_NONE) { 
     throw ELoggerContext("LOGGER_NONE cannot be instantiated"); 
    } 
} 

class LoggerContextNone : LoggerContext { 
private: 
     LoggerContextNone() { 
       mLA = LoggerArea::LOGGER_NONE; 
     } 
public: 
     virtual ~LoggerContextNone() override { 

     } 
     virtual std::string getContext() override { 
       return " "; 
     } 
     static LoggerContextNone& getInstance() { 
       static LoggerContextNone instance {}; 
       return instance; 
     } 
}; 

int main() { 
    // this should not be compilable: 
    LoggerContextNone n{LoggerArea::LOGGER_NONE}; 
    // this should at least throw an error at run time: 
    LoggerContext n{LoggerArea::LOGGER_NONE}; 
    return 0; 
} 

目標:

  • LoggerContextNone應該LoggerContext派生,因爲我們需要getGeneral()
  • LoggerContextNone應不可實例化getInstance(單例)
  • 基類肩d有沒有空的構造函數,但派生類應該有一個
  • LoggerContextNone不應該調用超構造函數,否則會拋出一個錯誤ELoggerContext
  • LoggerContext任何派生類不應該能夠覆蓋getGeneral()

實際上是否可以在C++中實現這一點?我正在尋找一個乾淨的解決方案(沒有工廠,...)

編譯器的錯誤是:

19_virtual_class.cpp: In constructor ‘LoggerContextNone::LoggerContextNone()’: 
19_virtual_class.cpp:45:22: error: no matching function for call to ‘LoggerContext::LoggerContext()’ 
    LoggerContextNone() { 
        ^
[...] 
19_virtual_class.cpp: In function ‘int main()’: 
19_virtual_class.cpp:62:46: error: no matching function for call to ‘LoggerContextNone::LoggerContextNone(<brace-enclosed initializer list>)’ 
    LoggerContextNone n{LoggerArea::LOGGER_NONE}; 
              ^

最後說明一點:這個模式似乎我的概念是很簡單:從一個基類派生許多類的另外還有一個默認類。

編輯:

如果我調整代碼由@Angew:

#include <string> 
#include <iostream> 
#include <stdexcept> 

using namespace std; 

enum class LoggerArea { 
     LOGGER_NONE, LOGGER_DOWNLOAD, LOGGER_COMPUTE, 
}; 

class ELoggerContext: std::runtime_error { 
     using std::runtime_error::runtime_error; 
}; 

class LoggerContextNone; 

class LoggerContext { 
protected: 
     LoggerArea mLA; 

     class LoggerContextNone_AccessToken 
     { 
       friend LoggerContextNone; 
       LoggerContextNone_AccessToken() {} 
     }; 
     explicit LoggerContext(LoggerContextNone_AccessToken) : mLA(LoggerArea::LOGGER_NONE) {} 
public: 
     LoggerContext(LoggerArea la); 
     virtual ~LoggerContext() = 0; 
     std::string getGeneral(); 
     virtual std::string getContext() = 0; 
}; 

string LoggerContext::getGeneral() { 
     string s = "general informations:"; 
     if (mLA==LoggerArea::LOGGER_NONE) { s += "LOGGER_NONE"; } 
     else if (mLA==LoggerArea::LOGGER_DOWNLOAD) { s += "LOGGER_DOWNLOAD"; } 
     else if (mLA==LoggerArea::LOGGER_COMPUTE) { s += "LOGGER_COMPUTE"; } 
     else { s += "??????????"; } 

     return s; 
} 
LoggerContext::LoggerContext(LoggerArea la) : 
       mLA(la) { 
    if (la == LoggerArea::LOGGER_NONE) { 
     throw ELoggerContext("LOGGER_NONE cannot be instantiated"); 
    } 
} 

class LoggerContextNone : LoggerContext { 
private: 
     LoggerContextNone(): LoggerContext(LoggerContextNone_AccessToken()) {} 
public: 
     virtual ~LoggerContextNone() override { 

     } 
     virtual std::string getContext() override { 
       return " "; 
     } 
     static LoggerContextNone& getInstance() { 
       static LoggerContextNone instance {}; 
       return instance; 
     } 
}; 

class LoggerContextDerived : LoggerContext { 
public: 
     virtual std::string getContext() override { 
       return "derived context"; 
     } 
}; 

int main() { 
    LoggerContextDerived n {LoggerArea::LOGGER_DOWNLOAD}; 

    // cout << "General : " << n.getGeneral() << endl; 
    // cout << "Context : " << n.getContext() << endl; 

    return 0; 
} 

我不能實例派生類。 g++說:

9_virtual_class.cpp: In function ‘int main()’: 
19_virtual_class.cpp:78:54: error: no matching function for call to ‘LoggerContextDerived::LoggerContextDerived(<brace-enclosed initializer list>)’ 
    LoggerContextDerived n {LoggerArea::LOGGER_DOWNLOAD}; 
                ^

而且它建議我使用複製構造函數或移動構造函數。 對我意味着什麼,構造

LoggerContext(LoggerArea la); 

是不是在派生類中可見。

回答

3

你可以達到你想要的結果,但不完全符合你的嘗試。有問題的要求是這一個:

LoggerContextNone不應該調用超構造函數,否則會拋出一個錯誤ELoggerContext

派生類將總是調用基類的構造函數。在C++中,如果沒有運行其構造函數,就無法合法地擁有一個類類型的有效對象。

但是,請注意它會調用一個構造函數的基數,這意味着它可以調用任意一個(派生類決定)。所以,你可以給基類專門用於使用由LoggerContextNone構造函數,像這樣:

class LoggerContext { 
protected: 
     LoggerArea mLA; 
     LoggerContext() : mLA(LOGGER_NONE) {} 
public: 
     LoggerContext(LoggerArea la); 
     virtual ~LoggerContext() = 0; 
     /* 
     static LoggerContext& getEmptyContext() { 
       static LoggerContextNone loggerContextNone = { LoggerArea::LOGGER_NONE }; 
       return loggerContextNone; 
     } 
     */ 
     std::string getGeneral(); 
     virtual std::string getContext() = 0; // pure virtual 
}; 

這將實現你想要的,但它將使從LoggerContext衍生來調用默認的構造函數的所有類,應該他們選擇這樣做。如果你想避免和只有使可用的構造函數LoggerContextNone,您可以用友誼的技巧和標籤派遣這樣做:

class LoggerContext 
{ 
protected: 
    class LoggerContextNone_AccessToken 
    { 
    friend LoggerContextNone; 
    LoggerContextNone_AccessToken() {} 
    }; 

    explicit LoggerContext(LoggerContextNone_AccessToken) : mLA(LOGGER_NONE) {} 
protected: 
    // ... the rest as before 
}; 

LoggerContextNone::LoggerContextNone() : LoggerContext(LoggerContextNone_AccessToken()) 
{} 

這意味着:

  1. 要調用LoggerContext的非投擲構造函數,您需要傳入一個LoggerContextNone_AccessToken對象。

  2. LoggerContextNone_AccessToken有一個私人構造函數,這意味着只有它的朋友可以構造它。

  3. LoggerContextNoneLoggerContextNone_AccessToken唯一的朋友,所以它是能夠構造LoggerContextNone_AccessToken,因此能夠調用的LoggerContext非投擲構造的唯一類唯一的類。


或者,你可以考慮一下你是否真的需要LoggerContextNone可言。也許你可以使LoggerContext的行爲爲LoggerContextNone,並且只允許派生類提供非無行爲。

+0

感謝您長期以來的答覆,但它接縫要有些東西仍然不適用於構造函數,請參閱我對該問題的編輯。 – LiPo

+1

@LiPo這是一個相互無關的問題。您不能使用基類ctor構造C++中的派生類。如果你想在派生類中使用單參數,你需要在那裏定義這樣的一個ctor。或者使用構造函數繼承('使用LoggerContext :: LoggerContext'),但那會繼承*所有*構造函數。 – Angew

+1

@LiPo您可能想了解更多關於C++構造函數如何在[好書](http://stackoverflow.com/q/388242/1782465)中工作的內容。 – Angew

0

來自@Angew的答案對於使模式起作用至關重要,但代碼中存在很多(而且還有一些)其他問題。

這是最好的,我可以做出來的,它仍然沒有工作,但也許在未來的日子裏,我得到它的100%:

#include <string> 
#include <iostream> 
#include <stdexcept> 

using namespace std; 

enum class LoggerArea { 
     LOGGER_NONE, LOGGER_DOWNLOAD, LOGGER_COMPUTE, 
}; 

class ELoggerContext: std::runtime_error { 
     using std::runtime_error::runtime_error; 
}; 

class LoggerContextNone; 

class LoggerContext { 
protected: 
     LoggerArea mLA; 

     class LoggerContextNone_AccessToken 
     { 
       friend LoggerContextNone; 
       LoggerContextNone_AccessToken() {} 
     }; 
     explicit LoggerContext(LoggerContextNone_AccessToken) : mLA(LoggerArea::LOGGER_NONE) {} 
public: 
     LoggerContext(LoggerArea la); 
     virtual ~LoggerContext() {}; 
     std::string getGeneral(); 
     virtual std::string getContext() = 0; 
}; 

string LoggerContext::getGeneral() { 
     string s = "general informations:"; 
     if (mLA==LoggerArea::LOGGER_NONE) { s += "LOGGER_NONE"; } 
     else if (mLA==LoggerArea::LOGGER_DOWNLOAD) { s += "LOGGER_DOWNLOAD"; } 
     else if (mLA==LoggerArea::LOGGER_COMPUTE) { s += "LOGGER_COMPUTE"; } 
     else { s += "??????????"; } 

     return s; 
} 
LoggerContext::LoggerContext(LoggerArea la) : 
       mLA(la) { 
    if (la == LoggerArea::LOGGER_NONE) { 
     throw ELoggerContext("LOGGER_NONE cannot be instantiated"); 
    } 
} 

class LoggerContextNone : LoggerContext { 
private: 
     LoggerContextNone(): LoggerContext(LoggerContextNone_AccessToken()) {} 
public: 
     virtual ~LoggerContextNone() override { 

     } 
     virtual std::string getContext() override { 
       return " "; 
     } 
     static LoggerContextNone* getInstance() { 
     // this was: 
     // static LoggerContextNone& getInstance() { 
       static LoggerContextNone instance {}; 
       return &instance; 
     } 
}; 

class LoggerContextDerived : public LoggerContext { 
public: 
     LoggerContextDerived(LoggerArea la):LoggerContext(la) { }; 
     virtual std::string getContext() override { 
       return "derived context"; 
     } 
     virtual ~LoggerContextDerived() override { 

     } 
}; 

int main() { 

    // test 1: derived class 
    LoggerContextDerived c1 {LoggerArea::LOGGER_DOWNLOAD}; // ok 

    cout << "General : " << c1.getGeneral() << endl;   // ok 
    cout << "Context : " << c1.getContext() << endl;   // ok 

    LoggerContext * c2 = &c1;        // ok 

    // test 2: derived none class 
    LoggerContextNone * c3 = LoggerContextNone::getInstance(); // ok 
    LoggerContext * c4 = c3;          // g++ error: 
    // error: ‘LoggerContext’ is an inaccessible base of ‘LoggerContextNone’ 
    LoggerContext * c5 = LoggerContextNone::getInstance();  // g++ error: 
    // error: ‘LoggerContext’ is an inaccessible base of ‘LoggerContextNone’ 

    return 0; 
}