C++ 如何设计一个初始化后保持不变且在整个程序中只存在一次的类
我很确定下面的问题在其他地方已经有了很好的答案,但是很难找到,因为我不知道问题的“名称” 我正在设计一个具有以下属性的类/对象/“某物”:C++ 如何设计一个初始化后保持不变且在整个程序中只存在一次的类,c++,design-patterns,static,C++,Design Patterns,Static,我很确定下面的问题在其他地方已经有了很好的答案,但是很难找到,因为我不知道问题的“名称” 我正在设计一个具有以下属性的类/对象/“某物”: 这是一种查找表 它在初始化后不会更改 它有几个非原始成员 它有一个复杂的初始化函数 整个计划也是如此 它由模板参数进行参数化 这听起来像是一个静态模板类: template <int T> class LookupTable{ public: static void init(){ // create entries
- 这是一种查找表
- 它在初始化后不会更改
- 它有几个非原始成员
- 它有一个复杂的初始化函数
- 整个计划也是如此
- 它由模板参数进行参数化
template <int T>
class LookupTable{
public:
static void init(){
// create entries depending on T
}
private:
static vector<Entries> entries;
}
模板
类可查找{
公众:
静态void init(){
//根据T创建条目
}
私人:
静态向量项;
}
我不喜欢的是我需要在程序中的某个地方调用init()
。因此,第一个问题是:如何使这个类完全自包含,而不需要在某个地方显式初始化?
第二部分:实现这样一个类的一般设计方法是什么?我非常乐意链接到一个好的示例
一个可能的候选人是单身汉。但我有一些疑问:
-单身汉在很多情况下认为设计不好。对于如上所述的查找表是否合适?
-Singleton有点长,因为我必须使用
LookupTable::getInstance()->getEntry(idx)
您应该能够通过拥有一个静态常量
实例来完成您想要的任务;您只需要给这个类一个默认构造函数(这相当于您的init()
函数)。如果您需要基于类型T
的不同构造函数,那么您可以为这些类型专门化LookupTable
话虽如此,有一个陷阱你应该意识到:。如果有其他引用可查找实例的
静态
对象,然后,您可能会遇到问题,因为它们的初始化顺序是未指定的。如果您想创建一个完全静态的类,而该类从未获得实例,并且只设置了一次,那么您应该能够使用所有静态函数并具有Init()
函数,该函数不返回任何内容,并确定是否已调用Init()。这只是对Singleton设计的一个调整
这样您就不必在代码中的某个地方调用Init()
,您可以将Init()
作为类中每个函数的第一行调用。因为Init()
如果已经调用了它,它将不会做任何更改。如果愿意,您甚至可以将Init()
设为私有
class StaticClass
{
public:
static void Init()
{
static bool created = false
if(!created)
{
// here we setup the class
created = true; // set to true so next time someone class Init() it is a do nothing operation.
}
}
//...
private:
StaticClass() {}
//...
};
由于无法获取StaticClass的实例,因为Init()
函数是空的,所以您确实不需要担心复制构造函数或赋值运算符。单例是模式,但是使用更安全的变体,这种方法可以避免静态初始化顺序失败和线程争用情况,并且由于您抱怨长度,我们可以通过get_entry函数进一步缩短它:
template <int T>
class LookupTable{
public:
static std::vector<Entry> make_entries(){ ...}
static const std::vector<Entry>& get_entries(){
static const std::vector<Entry> instances = make_entries();
return instances;
}
static const Entry& get_entry(size_t idx){
return get_entries()[idx];
}
};
模板
类可查找{
公众:
静态std::vector make_entries(){…}
静态常量std::vector&get_entries(){
static const std::vector instances=make_entries();
返回实例;
}
静态常量条目和获取条目(大小idx){
返回get_条目()[idx];
}
};
另一种避免单例的所有缺点的方法是不使用单例,只需直接将一个常规的旧类作为另一个参数传入即可。我用很多crc函数实现来做这件事,它们都有相对较重的表。。。大多数东西都不会在意,这样你就不必在设计模式上发疯了。而且速度更快。梅耶的独生子女来营救
template <class T>
struct LookupTable {
static LookupTable &get() {
static LookupTable lut;
return lut;
}
private:
LookupTable() {
// Your initialization
}
LookupTable(LookupTable const &) = delete;
LookupTable operator = (LookupTable const &) = delete;
};
模板
结构可查找{
静态LookupTable&get(){
静态可查找lut;
返回lut;
}
私人:
可查找的{
//您的初始化
}
LookupTable(LookupTable const&)=删除;
LookupTable运算符=(LookupTable常量&)=删除;
};
用法:
LookupTable<int>::get() // Will initialize on first call.
LookupTable::get()//将在第一次调用时初始化。
您可以重载运算符以简化索引,甚至可以将其隐藏在get()
中
我正在设计一个具有以下属性的类/对象/“某物”:
•它是一种查找表
•初始化后不会发生变化
客户端代码:
const LookupTable lookup_table = ...;
^^^^^
const auto lookup_table = make_lookup_table();
•它有几个非原始成员
•整个计划都是一样的
在使用依赖项注入的代码中使用依赖项注入
•通过模板参数对其进行参数化
只需根据需要向上面的代码中添加模板参数即可
注意事项:
- 代码中没有任何内容表明将存在单个实例。这是类(客户机代码)用法的一部分,而不是它的定义
- 这不是单身汉。从许多角度来看,单例是反模式的
- 将来可能需要定义该类的多个实例(可能用于单元测试);这里没有任何东西可以阻止你这么做
- 复杂的初始化部分集中(隐藏)在工厂函数中。如果初始化失败,则不会构造任何实例。如果初始化更改,则类的公共接口不会更改。如果您需要在不同的情况下添加不同的初始化(调试与发布、测试与生产、快速与安全运行时配置),则无需删除或修改现有代码-只需添加新的factory函数
如果您可以使用C++14进行编译,请
class LookupTable
{
std::vector<Entry> entries;
^^^^^^^^^^^^^^^^^^^^^^^^^^^
};
class LookupTable
{
std::vector<Entry> entries;
^^^^^^^^^^^^^^^^^^^^^^^^^^^
public:
explicit LookupTable(
std::vector<Entry> e
// if more members are required, receive them here,
// fully constructed
): entries{ std::move(e) } {}
};
LookupTable make_lookup_table()
{
std::vector<Entry> entries;
// perform complicated value initialization here
// and once everything is initialized, pass to new instance of
// LookupTable which is returned
return LookupTable{ std::move(entries) };
}
const auto lookup_table = make_lookup_table();
// Complicated initializer function that create entries depending on T
// could be specialized for T.
template <int T>
constexpr std::vector<Entries> init() { return {T, Entries{}}; }
// Class with several non-primitive members.
template <int T>
class LUT {
public:
constexpr LUT() : entries{init<T>()} {}
auto foo() const { return entries.size(); }
const void *bar() const { return entries.data(); }
const void *baz() const { return this; }
private:
std::vector<Entries> entries;
};
// Variable template parametrized by template parameters.
// It will be the same for the whole program.
template <int T>
LUT<T> LookupTable{};
void f15() { std::cout << LookupTable<15>.foo() << '\n'; }
void f5() { std::cout << LookupTable<5>.foo() << '\n'; }
int main()
{
// LookupTable<15> is the same here and in f15
std::cout << LookupTable<15>.foo() << ' '
<< LookupTable<15>.bar() << ' '
<< LookupTable<15>.baz() << '\n';
// LookupTable<5> is the same here and in f5
std::cout << LookupTable<5>.foo() << ' '
<< LookupTable<5>.bar() << ' '
<< LookupTable<5>.baz() << '\n';
return 0;
}