C++模式:1x基类+Nx派生类,但具有_上一个资源派生类
C++ pattern: 1x base class + Nx derived classes BUT with a _last resort_ derived class
我正在尝试实现一个具有三个信息级别的记录器:常规(日期/时间)、上下文、消息
为了达到这个目标,我试图实现以下模式:
- 记录器类(此处不相关)
- 上下文类
- 基类
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);
在派生类中不可见。
您可以实现您想要的结果,但不能完全按照您尝试的方式。有问题的需求是:
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
一样,只允许派生类提供non-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;
}
- 类模板上一个特定函数的未解析外部符号
- 错误:"int pow(double,int)"与上一个声明int pov(double a,int
- 如何查看提升集中<cpp_int>的下一个和上一个元素
- 我想知道我将如何实现 + 运算符重载.我已经从我上一个问题中计算出 += 运算符重载
- 如何删除上一个节点?
- 插入指针数组会删除上一个结果
- 助推器使用上一个DMATRIX后,XGDMATRIXCREATEFROMMAT失败
- 尝试指向链表中的上一个节点
- 如何在C++中找到上一个星期一的日期
- 与上一个线程具有相同ID的线程同步
- 无法从派生的一个方法调用基类方法
- 是否可以在同一设备缓冲区上一个接一个地调用 OpenCL 内核?
- 只有在上一个线程返回后才启动一个新的"wxThread"
- 我如何使用上一个块的哈希链接2个块
- C libcurl-使用上一个卷曲的响应作为参数到另一个卷曲的参数
- 您为什么要在班上一个结构
- sfml pollevent,每个新图像都会删除上一个图像
- 流媒体错误:在退出上一个呼叫之前,请再次调用InternetStatusCallback
- 上一个较大的数字代码中的输出不正确
- C++模式:1x基类+Nx派生类,但具有_上一个资源派生类