C ++模式:1x基类+ Nx派生类但是_last resort_派生类

时间:2016-05-17 07:39:02

标签: c++ c++11 design-patterns

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

为了达到这个目标,我正在尝试实现以下模式:

  1. 记录器类(此处不相关)
  2. 上下文类
    • 基类LoggerContext,具有生成常规级别信息的功能
    • 派生类,添加特定于上下文的信息(特定于应用程序的一部分)
  3. 有趣的部分从我尝试拥有 none 上下文开始。也就是说,如果在没有上下文的情况下调用Logger,则应使用单个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(singleton)
    • 之外进行实例化
    • 基类应该没有空构造函数,但派生类应该有一个
    • 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);
    

    在派生类中不可见。

2 个答案:

答案 0 :(得分: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

  4. 或者,您可以考虑是否确实需要LoggerContextNone。也许你可以让LoggerContext表现为LoggerContextNone,并且只允许派生类提供非none行为。

答案 1 :(得分: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;
}