我正在尝试实现一个包含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(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);
在派生类中不可见。
答案 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())
{}
这意味着:
要调用LoggerContext
的非投掷构造函数,您需要传入LoggerContextNone_AccessToken
对象。
LoggerContextNone_AccessToken
有一个私有构造函数,这意味着只有它的朋友可以构造它。
LoggerContextNone
是LoggerContextNone_AccessToken
的唯一朋友,因此它是唯一能够构造LoggerContextNone_AccessToken
的类,因此是唯一能够调用非投掷构造函数的类LoggerContext
。
或者,您可以考虑是否确实需要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;
}