C++ C++;模式:1x基类+;Nx派生类,但具有_最后手段_派生类

C++ C++;模式:1x基类+;Nx派生类,但具有_最后手段_派生类,c++,c++11,design-patterns,C++,C++11,Design Patterns,我正在尝试实现一个具有3个级别信息的记录器:常规(日期/时间)、上下文和消息 为了实现这一目标,我尝试实现以下模式: 记录器类别(此处不相关) 上下文类 基类LoggerContext,具有生成一般级别信息的功能 派生类,它添加特定于上下文的信息(特定于应用程序的一部分) 有趣的部分是从我尝试无上下文开始的。也就是说,如果在没有上下文的情况下调用记录器,则应使用单例LoggerContextNone 在这里,我的代码,无论我如何转换,都不会编译: #include <string>

我正在尝试实现一个具有3个级别信息的记录器:常规(日期/时间)、上下文和消息

为了实现这一目标,我尝试实现以下模式:

  • 记录器类别(此处不相关)
  • 上下文类
    • 基类
      LoggerContext
      ,具有生成一般级别信息的功能
    • 派生类,它添加特定于上下文的信息(特定于应用程序的一部分)
  • 有趣的部分是从我尝试无上下文开始的。也就是说,如果在没有上下文的情况下调用记录器,则应使用单例
    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;
    }
    
    最后一点注意:这个模式在概念上似乎很简单:许多类都是从一个基类派生的,另外还有一个默认类

    编辑:

    如果我通过@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;
    }
    
    它建议我使用复制构造函数或移动构造函数。 这对我来说意味着构造函数

    LoggerContext(LoggerArea la);
    

    在派生类中不可见。

    您可以实现所需的结果,但不完全是您尝试过的方式。有问题的要求是:

    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())
    {}
    
    这意味着:

  • 要调用
    LoggerContext
    的非抛出构造函数,需要传入
    LoggerContextNone\u AccessToken
    对象

  • LoggerContextNone\u AccessToken
    有一个私有构造函数,这意味着只有它的朋友才能构造它

  • LoggerContextNone
    LoggerContextNone\u AccessToken
    的唯一朋友,因此它是唯一能够构造
    LoggerContextNone\u AccessToken
    的类,也是唯一能够调用
    LoggerContext
    的非抛出构造函数的类



  • P>可选的,你可以考虑你是否真的需要< LoggerContextNone > >代码>。也许您可以让
    LoggerContext
    表现为
    LoggerContextNone
    ,只允许派生类提供非none行为。

    来自@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;
    }
    
    #包括
    #包括
    #包括
    使用名称空间std;
    枚举类日志区域{
    记录器\u无,记录器\u下载,记录器\u计算,
    };
    类ELoggerContext:std::runtime\u错误{
    使用std::runtime\u error::runtime\u error;
    };
    类LoggerContextNone;
    类LoggerContext{
    受保护的:
    LoggerArea mLA;
    类LoggerContextNone_AccessToken
    {
    罗格·康特斯顿朋友;
    LoggerContextNone_AccessToken(){}
    };
    显式LoggerContext(LoggerContextNone_AccessToken):mLA(LoggerArea::LOGGER_NONE){}
    公众:
    LoggerContext(LoggerArea la);
    virtual~LoggerContext(){};
    std::string getGeneral();
    虚拟std::string getContext()=0;
    };
    字符串LoggerContext::getGeneral(){
    string s=“一般信息:”;
    如果(mLA==LoggerArea::LOGGER_NONE){s+=“LOGGER_NONE”;}
    else if(mLA==loggerea::LOGGER_下载){s+=“LOGGER_下载”;}
    else如果(mLA==loggerea::LOGGER\u COMPUTE){s+=“LOGGER\u COMPUTE”;}
    否则{s+=“?”;}
    返回s;
    }
    LoggerContext::LoggerContext(LoggerArea la):
    法学硕士(洛杉矶){
    if(la==loggerea::LOGGER_NONE){
    抛出Elogger上下文(“LOGGER_NONE无法实例化”);
    }
    }
    类LoggerContextNone:LoggerContext{
    私人:
    LoggerContextNone():LoggerContext(LoggerContextNone_AccessToken()){}
    公众:
    虚拟~LoggerContextNone()覆盖{
    }
    虚拟std::string getContext()重写{
    返回“”;
    }
    静态LoggerContextNone*getInstance(){
    //这是:
    //静态LoggerContextNone&getInstance(){
    静态LoggerContextNone实例{};
    返回&实例;
    }
    };
    类LoggerContext派生:公共LoggerContext{
    公众:
    LoggerContextDerived(loggerareala):LoggerContext(la){};
    虚拟std::string getContext()重写{
    返回“派生上下文”;
    }
    virtual~LoggerContextDerived()重写{
    }
    };
    int main(){
    //测试1:派生类
    LoggerContext派生c1{LoggerArea::LOGGER_DOWNLOAD};//确定
    
    CUT感谢您的长时间回复,但它是因为某些东西仍然没有与构造函数一起工作,请参阅我的问题编辑。@ LICOO是一个相当无关的问题。您不能使用基类CCTR来构造C++中的派生类。如果您想在派生类中使用一个PARAM CCTR,则需要在那里定义这样的Cor。构造函数继承(
    使用LoggerContext::loggercontent)
    
    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())
    {}
    
    #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;
    }