- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试实现一个包含 3 级信息的记录器:一般(日期/时间)、上下文、消息
为了达到这个目标,我试图实现以下模式:
LoggerContext
,具有生成通用级别信息的功能有趣的部分开始于我尝试拥有一个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 之外实例化(单例)LoggerContextNone
不应该调用 super 构造函数,否则会抛出错误 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);
在派生类中不可见。
最佳答案
您可以获得想要的结果,但与您尝试过的方式不同。有问题的要求是这个:
LoggerContextNone
should not call the super constructor, otherwise it would throw an errorELoggerContext
派生类总是调用基类的构造函数。在 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
,您可以使用友元技巧和标记分派(dispatch)来做到这一点:
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_AccessToken
对象。
LoggerContextNone_AccessToken
有一个私有(private)构造函数,这意味着只有它的 friend 可以构造它。
LoggerContextNone
是 LoggerContextNone_AccessToken
的唯一 friend ,因此它是唯一能够构造 LoggerContextNone_AccessToken
的类,因此也是唯一的能够调用 LoggerContext
的非抛出构造函数的类。
或者,您可以考虑是否真的需要 LoggerContextNone
。也许您可以让 LoggerContext
表现得像 LoggerContextNone
,并且只允许派生类提供非无行为。
关于C++ 模式 : 1x base class + Nx derived classes BUT with a _last resort_ derived class,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37269978/
我正在尝试实现一个包含 3 级信息的记录器:一般(日期/时间)、上下文、消息 为了达到这个目标,我试图实现以下模式: 记录器类(此处不相关) 上下文类 基类LoggerContext,具有生成通用级别
我是一名优秀的程序员,十分优秀!