最近我在SO上发布了一个关于类的使用的问题,该类具有一些理论上应该具有的独立功能。我被建议学习单例模式,以便只创建一个类的实例,它管理围绕它封装的数据的操作集。您可以在此处查看问题 - using Static Container for base and derived classes。
现在考虑一下这段代码 -
#include <iostream>
#include <string>
#include <unordered_map>
class A{
std::string id;
public:
A(std::string _i): id(_i){}
virtual void doSomething(){std::cout << "DoSomethingBase\n";}
};
class B : public A{
std::string name;
public:
B(std::string _n):name(_n), A(_n){}
void doSomething(){std::cout << "DoSomethingDerived\n";}
};
namespace ListA{
namespace{
std::unordered_map<std::string, A*> list;
}
void init(){
list.clear();
}
void place(std::string _n, A* a){
list[_n] = a;
}
}
int main() {
ListA::init();
ListA::place("b1", new B("b1"));
ListA::place("a1", new A("a1"));
return 0;
}
如果程序没有像那样终止,忽略了我仍在使用泄漏内存的原始指针的事实,这是使用全局静态变量的一个很好的替代方法,还是单身?
关于上一个问题,我重新组织了A类(基类)和B类(派生类),独立于管理这些对象列表的命名空间。这是一个好主意,还是一个完全不好的做法?它有什么缺点吗?
我建议的一个好的单例实现如下 -
class EmployeeManager
{
public:
static EmployeeManager& getInstance()
{
static EmployeeManager instance; // Guaranteed to be destroyed.
// Instantiated on first use.
return instance;
}
private:
EmployeeManager() {};
std::unordered_map<std::string, Employee&> list;
public:
EmployeeManager(EmployeeManager const&) = delete;
void operator=(const&) = delete;
void place(const std::string &id, Employee &emp){
list[id] = emp;
}
};
class Employee
{
public:
virtual void doSomething() = 0;
};
class Writer : public Employee
{
private:
std::string name_;
public:
Writer(std::string name) : name_(name) {};
void doSomething() { };
};
老实说,我从来没有尝试过单身模式,而且因为没有先前的经验而我不想直接使用它,我宁愿先在我的宠物项目中使用它。
答案 0 :(得分:2)
这是使用全局静态变量或单例的好方法吗?
不,因为您可能会遇到另一个问题:static initialization order fiasco。有办法解决它 - 但使用静态变量的函数 - 看起来就像单身。
...但为什么你需要全局变量(甚至在命名空间中)或单身?在第一个示例中,如果您namespace ListA
代替struct ListA
而不是删除namespace{
,那将完全没问题。然后你有:
int main() {
ListA list;
list.init();
list.place("b1", new B("b1"));
list.place("a1", new A("a1"));
}
它看起来很好。
然后你的单例aproach,再次 - 不需要它 - 在你的main函数中创建类型EmployeeManager
的变量,如果你需要在其他类中使用它,然后通过引用或指针传递它。 / p>
答案 1 :(得分:1)
我不确定你是否已经知道,但是你需要记住 Singleton 确实是 一个具有延迟初始化的全局变量 强>
延迟初始化是一种工具,用于解决在您真正想要使用它时始终初始化对象的问题 - 无论是对于某些实际程序函数还是初始化另一个依赖对象。这样做是为了将初始化延迟到使用对象的第一个时刻。
静态对象只是在需要创建第一个的时刻进行初始化 - 但是当这个时刻确实存在时,是未定义的,至少在C ++中是这样。
您可以使用静态初始化替换延迟初始化,但必须确保初始化以定义的顺序发生。
在命名空间内定义变量只不过是全局声明变量。命名空间是开放的,命名空间内的规则与命名空间外的规则相同,除了符号解析。
强制执行有序初始化的目的是创建一个全局变量,其中包含所有依赖的全局对象,形式为struct
,将所有对象都包含为字段(不静态领域!)。请注意,只有在作为该结构的字段的对象之间才能确保初始化的确切顺序,而不是在它们与任何其他全局对象之间。
答案 2 :(得分:0)
您的问题可以在没有任何代码的情况下得到解答,因为过去很多人都回答过。单身人士很糟糕,因为你的代码将依赖于一个类及其实现。你想要的是拥有独立的单位,他们不了解他们交谈的接口的实现。值/引用的传播应该(实际上必须对大型可维护系统进行)通过从包含对象传递到其子节点,观察者/事件系统或事件/消息总线的引用。许多框架都使用这些方法中的两种......我强烈建议坚持使用最佳实践。