2016-05-17 71 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; 
}