如何设计初始化后不变的类,并且在我的整个程序中仅存在一次
How to design a class that is constant after initialization and exists only once in my whole program
我很确定以下问题已经在其他地方有一个很好的答案,但是很难找到问题,因为我不知道我的问题的"名称"。
我正在设计一个具有以下属性的类/对象/"某物":
- 这是一个查找表。
- 初始化后不会改变。
- 它有几个非主要成员。
- 它具有复杂的初始化功能。
- 整个程序都是一样的。
- 它是由模板参数进行参数化的。
所以这听起来像静态模板类:
template <int T>
class LookupTable{
public:
static void init(){
// create entries depending on T
}
private:
static vector<Entries> entries;
}
我不喜欢的是我需要在程序中的某个地方调用init()
。因此,第一个问题是:如何使此类完全独立,而无需在某个地方明确初始化?
第二部分:实现此类类的一般设计方法是什么?我对一个好示例的链接感到非常满意。
可能的候选人是单身人士。但是我有一些疑问: - 单身人士在许多情况下被认为是不良设计。如上所述的查找表是否可以? - 单例有点长,因为我必须使用 LookupTable::getInstance()->getEntry(idx)
。
singleton是模式,但是使用更安全的变体,该方法避免静态初始化顺序惨败和线程种族条件,并且由于您抱怨长度 - 我们可以将其进一步缩短,然后通过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];
}
};
另一种避免单身邪恶的方法不是使用单身人士 - 只需将普通的旧班直接作为另一个参数即可。我使用许多CRC函数实现了相对较重的表...大多数东西都不在乎,然后您不必在设计模式上摇摆。而且它更快。
我正在设计一个具有以下属性的类/对象/"某物":
•这是一个查找表。
class LookupTable
{
};
•初始化后不会改变。
客户端代码:
const LookupTable lookup_table = ...;
^^^^^
•它有几个非竞争成员。
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();
•整个程序都是相同的。
在使用它的代码中使用依赖项注入。
•通过模板参数进行了参数。
只需在需要时将模板参数添加到上面的代码。
要注意的事情:
代码中没有任何建议将存在一个实例。这是类使用(客户端代码)的一部分,而不是定义。
这不是单身人士。Singleton是(从许多角度来看)和对抗。
您可能需要在将来定义类的多个实例(可能用于单位测试);这里没有什么可以阻止您这样做。
复杂的初始化部分是在出厂功能中集中(和隐藏)的。如果初始化失败,则不会构建实例。如果初始化发生了变化,则类的公共接口不会更改。如果您需要在不同情况下添加不同的初始化(调试与发布,测试与生产,快速与安全运行时配置),则无需删除或修改现有代码 - 只需添加新的出厂功能即可。
如果要完成一个完全静态的类,该类是您永远不会得到实例的,并且仅设置了一次,那么您应该能够使用全部静态功能并具有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()
函数是无效的,因此您实际上不必担心复制构造函数或分配运算符。
Meyer的Singleton进行营救!
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<int>::get() // Will initialize on first call.
您可以超载操作员简化索引,甚至可以将其隐藏在get()
中。
如果您可以使用C 14编译,您是否考虑过使用变量模板?
// 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;
}
它是否达到了您的要求?
- 这是一个查找表:我不知道,取决于
LUT
实现。 - 初始化后不会更改:一旦初始化了
LookupTable
(在调用main
之前),它无法更改*,请务必将所有LUT
标记为const
也。 - 它有几个非启示成员:我不知道,取决于
LUT
实施。 - 它具有复杂的初始化器函数:使
init()
的函数随意进行复杂,但考虑到它会在静态初始化期间调用。 - 整个程序的相同:每个
LookupTable<NUMBER>
的每个 CC_19实例都相同。 - 它是通过模板参数进行参数化的:afaik是。
希望它能帮助 demo
*我不知道为什么template <int T>
const
LUT<T> LookupTable{};
失败,但是无论如何LUT
缺少operator =
。">
您应该能够通过仅使用static const
实例来完成想要的工作;您只需要给班级一个默认的构造函数(这等同于您的init()
函数)。如果您需要基于T
类型的不同构造函数,则可以专门为这些类型的LookupTable<T>
。
话虽如此,您应该知道一个陷阱:静态初始化顺序惨败。如果您有其他static
对象,请参考LookupTable<T>
实例,则可以遇到问题,因为未指定它们的初始化顺序。
- 当我在main中声明了我的2d数组时,为什么我的程序会退出
- 为什么我的 IExtractIcon 处理程序没有被调用?
- 为什么我的程序在for循环中k=0时返回垃圾值
- 为什么它只打印双链接列表的第一个值,而我的程序却崩溃了
- 我的程序没有从文件中读取并输入数据
- 我的评分程序无法正常工作
- 为什么我的C++程序的程序集输出充满了 .ascii,没有汇编代码?
- 我的程序有一个保存配置文件的GUI,如何双击此配置文件以直接加载带有配置数据的GUI?
- 我的目标是编写一个程序来计算和存储字符串在字符数组中出现的位置
- 反向功能超出了我的 cpp 程序的范围
- 我的二进制搜索程序只是关闭了
- 为什么我的程序不能显示斐波那契级数?
- 为什么我的程序在使用预留后没有加速?
- 为什么如果我添加这一行,我的程序会不断询问值
- 为什么当我输入较大的数字时,我的程序会到达文件末尾?
- 为什么我的两个 cin 语句没有在程序结束时运行?
- 这是使用回溯的 nqueen 问题,但我使用了动态 2d 数组,我的程序编译良好,但不返回任何输出
- 为什么我的程序在 O0 和 O2 的优化级别返回不同的结果
- C++机器人程序(我的私人服务器)出现问题
- 用C++创建一个写作应用程序(我的第一个项目)(学习C++)