如何在单例中传递参数
Posted
技术标签:
【中文标题】如何在单例中传递参数【英文标题】:How to pass argument in a singleton 【发布时间】:2014-02-19 03:40:49 【问题描述】:我一直想知道如何将参数传递给单例构造函数。我已经知道如何做单例了,但是我很不幸地找到了一种方法。
这是我的代码(部分)。
Questionnary* Questionnary::getInstance()
static Questionnary *questionnary = NULL;
if(questionnary == NULL)
cout << "Object created";
questionnary = new Questionnary();
else if(questionnary != NULL)
cout << "Object exist";
return questionnary;
Questionnary::Questionnary()
cout << "I am an object";
//This is want i want to acheive
Questionnary::Questionnary(string name)
cout << "My name is << name;
在此先感谢
(顺便说一句,我知道如何以及为什么单身不好)
【问题讨论】:
既然你已经有了一个单例,创建几个全局变量并没有什么坏处,每个构造函数参数一个,然后在第一次调用getInstance()
之前为所有这些变量赋值。并且不需要new
单例实例,只要在getInstance()
内部做一个静态变量,C++11甚至保证它的初始化是线程安全的。
【参考方案1】:
您不需要动态分配单例的实例。它可能看起来像以下方式(这有时被称为“延迟加载单例”~实例创建较晚并且有点“自动”):
#include <iostream>
#include <string>
class Questionnary
private:
// constructor taking string:
Questionnary(const std::string& name) : name_(name)
public:
static Questionnary& getInstance(const std::string& name)
static Questionnary q(name);
std::cout << "My name is: " << q.name_ << std::endl;
return q;
private:
std::string name_;
;
int main()
Questionnary::getInstance("Josh");
Questionnary::getInstance("Harry");
输出:
My name is: Josh
My name is: Josh
请注意,当第一次调用getInstance
时,构造函数只会被调用一次。
【讨论】:
不动态分配单例是正确的想法,但如果你要直接分配数据成员,那么有构造函数参数有什么意义? @Praetorian:这是为了指出传递的字符串也可能用于在下一次getInstance
调用中更改该单例的状态,但你是对的,它使构造函数在这里没用。
谢谢,所以如果我做对了,我只需要传递我的参数就是将它们放在 getInstance 函数中,然后将它传递给我的构造函数。
如何向此实现添加不带参数的 getInstance 方法? (只是为了稍后获取实例,不需要传递参数)
我不明白为什么这是公认的答案,因为它展示了如何不将参数传递给单例的构造函数。此外,要求传递参数,即使除了第一次调用之外的所有参数都会被忽略,这是造成混乱的主要来源【参考方案2】:
让我为您的用例扩展Martin York's answer。我建议在这种特殊情况下使用指针作为参数,因为我们利用它的固有属性,它可以是“空的”。
class Questionnary
std::string _str;
static Questionnary& getInstanceImpl(std::string* const s = nullptr)
static Questionnary instance s ;
return instance;
Questionnary(std::string* const s)
: _str s ? move(*s) : std::string // employ move ctor
if (nullptr == s)
throw std::runtime_error "Questionnary not initialized" ;
public:
static Questionnary& getInstance()
return getInstanceImpl();
static void init(std::string s) // enable moving in
getInstanceImpl(&s);
Questionnary(Questionnary const&) = delete;
void operator=(Questionnary const&) = delete;
;
我发现这种方法不那么令人困惑,因为它让您在第一次初始化后获得实例而无需(无论如何丢弃)参数:
// first init
Questionnary::init("my single Questionnary");
// later on ...
Questionnary& q = Questionnary::getInstance();
编辑:从 getInstance 函数中删除了参数并进行了一些优化。
【讨论】:
它比接受的答案更好,因为您不必传递参数,但我仍然不喜欢您可以传递参数(如果实例已经存在,则会被忽略)。 @user463035818 我编辑了代码,以免在getInstance
函数中使用未使用的参数分散用户的注意力。你可以接受这种形状吗?
不错的解决方案,但我认为您的编辑应该是Questionnary::init(s)
。【参考方案3】:
有一个方法来创建实例以将参数传递给构造函数,如果在调用它之前没有调用 CreateInstance,您可以在 getInstance() 方法中断言。喜欢:
class Questionnary
private:
// constructor taking string:
Questionnary(const std::string& name) : name_(name)
std::cout << "My name is: " << q.name_ << std::endl;
static Questionnary* m_instance;
public:
static void createInstance(const std::string& name)
assert(!m_instance);
m_instance = new Questionary(name);
static void destroyInstance()
assert(m_instance);
delete m_instance;
static Questionnary* Questionnary::getInstance()
assert(m_instance);
return m_instance;
private:
std::string name_;
;
【讨论】:
很好,但是如果构造过程需要创建本身引用getInstance()
的对象,它会变得混乱......但我想我们不应该这样做 ;-)同时,我使用bool
进行断言,并在调用new
之前设置此,以便assert
在期间 构造成功。 (为了让这一切变得更加丑陋,我使用了位置new
...我是个怪物)【参考方案4】:
我的版本使用现代 C++,包装现有类型:
#ifndef SINGLETON_H
#define SINGLETON_H
template <typename C, typename ...Args>
class singleton
private:
singleton() = default;
static C* m_instance;
public:
~singleton()
delete m_instance;
m_instance = nullptr;
static C& instance(Args...args)
if (m_instance == nullptr)
m_instance = new C(args...);
return *m_instance;
;
template <typename C, typename ...Args>
C* singleton<C, Args...>::m_instance = nullptr;
#endif // SINGLETON_H
这是单元测试中的样子:
int &i = singleton<int, int>::instance(1);
UTEST_CHECK(i == 1);
tester1& t1 = singleton<tester1, int>::instance(1);
UTEST_CHECK(t1.result() == 1);
tester2& t2 = singleton<tester2, int, int>::instance(1, 2);
UTEST_CHECK(t2.result() == 3);
问题在于 instance() 在每次调用时都需要参数,但只在第一次调用时使用它们(如上所述)。一般不能使用默认参数。最好使用 create(Args...) 方法,该方法必须在调用 instance() 或引发异常之前。
【讨论】:
有趣。我也喜欢你如何设法在你的答案中提到单元测试。这是一个经常被遗忘的方面。 也许我在这里很无知,但是如何从静态方法中访问成员变量呢?这是更现代的 C++ 标准的一个特性吗? 我没有访问成员变量。它被贴错标签。我应该将其标记为 s_instance【参考方案5】://! @file singleton.h
//!
//! @brief Variadic template to make a singleton out of an ordinary type.
//!
//! This template makes a singleton out of a type without a default
//! constructor.
#ifndef SINGLETON_H
#define SINGLETON_H
#include <stdexcept>
template <typename C, typename ...Args>
class singleton
private:
singleton() = default;
static C* m_instance;
public:
singleton(const singleton&) = delete;
singleton& operator=(const singleton&) = delete;
singleton(singleton&&) = delete;
singleton& operator=(singleton&&) = delete;
~singleton()
delete m_instance;
m_instance = nullptr;
static C& create(Args...args)
if (m_instance != nullptr)
delete m_instance;
m_instance = nullptr;
m_instance = new C(args...);
return *m_instance;
static C& instance()
if (m_instance == nullptr)
throw std::logic_error(
"singleton<>::create(...) must precede singleton<>::instance()");
return *m_instance;
;
template <typename C, typename ...Args>
C* singleton<C, Args...>::m_instance = nullptr;
#endif // SINGLETON_H
和:
void
singleton_utest::test()
try
singleton<int, int>::instance();
UTEST_CHECK(false);
catch (std::logic_error& e)
UTEST_CHECK(true);
try
UTEST_CHECK((singleton<int, int>::create(1) == 1));
UTEST_CHECK((singleton<int, int>::instance() == 1));
catch (...)
UTEST_CHECK(false);
using stester0 = singleton<tester0>;
try
stester0::instance();
UTEST_CHECK(false);
catch (std::logic_error& e)
UTEST_CHECK(true);
try
UTEST_CHECK((stester0::create().result() == 0));
UTEST_CHECK((stester0::instance().result() == 0));
catch (...)
UTEST_CHECK(false);
using stester1 = singleton<tester1, int>;
try
stester1::instance();
UTEST_CHECK(false);
catch (std::logic_error& e)
UTEST_CHECK(true);
try
UTEST_CHECK((stester1::create(1).result() == 1));
UTEST_CHECK((stester1::instance().result() == 1));
catch (...)
UTEST_CHECK(false);
using stester2 = singleton<tester2, int, int>;
try
stester2::instance();
UTEST_CHECK(false);
catch (std::logic_error& e)
UTEST_CHECK(true);
try
UTEST_CHECK((stester2::create(1, 2).result() == 3));
UTEST_CHECK((stester2::instance().result() == 3));
catch (...)
UTEST_CHECK(false);
【讨论】:
以上是关于如何在单例中传递参数的主要内容,如果未能解决你的问题,请参考以下文章
在单例中使用 BroadcastReceiver 的最巧妙方法等等
如何检查 googlemock 中作为 void 指针传递的字符串参数