【问题标题】:C++ Unique ObjectsC++ 唯一对象
【发布时间】:2011-03-24 19:49:15
【问题描述】:

我想创建一个类,其中可能的实例范围有限,并且用户无法创建新实例。例如,货币是独一无二的,我正在工作的图书馆的用户不应该能够创建新货币或复制现有货币。我猜这有点像多吨模式。到目前为止我所拥有的是:

#include <string>
#include <boost/smart_ptr/shared_ptr.hpp>

struct Currency {
public:
    typedef const Currency * Pointer;

    std::string code;

    static const Currency & Get(const std::string & Code);
private:
    Currency();
    Currency(const std::string & c);
};

Currency::Currency(const std::string & c)
:code(c) {}

const Currency & Currency::Get(const std::string & Code) {
    typedef boost::shared_ptr<Currency> Value;
    typedef std::map<std::string, Value> MapType;
    static std::map<std::string, Value> map_;

    if (map_.empty()) {
        // Initialize your map here, from a database query or what have you...
    }
    MapType::const_iterator it = map_.find(Code);
    if (it == map_.end()) {
        throw std::exception(("[Currency::Get] Currency '" + Code + "' not found").c_str());
    }
    return *it->second;
}

这个设计有什么明显的问题吗? (我知道这不是线程安全的) 是否有一种我不知道的普遍接受的技术/模式传统上用于实现这一目标?

谢谢, 马克。

【问题讨论】:

  • 听起来您正在寻找工厂模式,en.wikipedia.org/wiki/Factory_method_pattern。您的 Get() 函数通常称为 CreateInstance() 函数,但您的想法是合理的。
  • 地图会保存一份自己的字符串副本,所以公开:std::string 代码;应该用吸气剂代替。此外,工厂本身并不限制可用对象的数量。为什么要限制实例?
  • 这不完全是工厂模式。我希望能够通过比较它们在内存中的地址来廉价地比较对象。即,我有两个现金流,它们是同一种货币吗? operator==(const Currnecy & rhs) 就变成了 {this == &rhs);}。在这种情况下,重要的是要确保不会无意中创建新的美元实例。
  • @Hazerider:你已经拥有了 90% 的工厂模式。你只需要调整它!
  • 我想这真的是工厂和单身的混合体:-)

标签: c++ constructor unique instance private


【解决方案1】:

查看boost:noncopyable 以防止复制;这是一个很好的参考解决方案,您可以信任并从中学习。防止实例化很容易:private 构造函数。

【讨论】:

  • 我还能将 boost::noncopyable 对象放入地图吗?
  • @Hazerider:您可以将指向它们的指针存储到地图中。通过将析构函数设为私有,您可以确保没有人可以delete 这些指针。
  • 构造函数看起来已经是私有的了。此外,您也可以通过将复制构造函数设为私有来廉价地防止复制。
【解决方案2】:

声明一个私有复制构造函数Currency::Currency(const Currency&amp;),但不要定义它。这可以防止用另一个 Currency 对象初始化 Currency 对象。同样,声明一个私有赋值运算符Currency&amp; Currency::operator=(const Currency&amp;)。这样您就可以防止将一种货币分配给另一种货币。

【讨论】:

  • 将这些设为私有足以防止分配和复制。我相信我需要实现两者之一,否则我不能使用 std::map 来存储实例。
  • 上面的评论很傻。我显然无法将实例存储在地图中。要在任何类型的容器中使用它们,您必须存储指向它们的指针(如 Jon 上面提到的)。足够聪明的人可能会创建滥用它的代码,但我很确定这很难无意中做到。
【解决方案3】:

我相信您所说的是单例模式。所以你可以阅读它。

大多数语言中获取单例的方式是

1) 将构造函数设为私有

2) 在类内部实例化你的类的单个实例(单例)(私有静态)

3) 创建一个静态(类)方法,该方法返回指向在第 2 步中创建的静态私有实例的指针(通常称为 getInstance() 或 instance() 或类似名称。)

任何需要使用这个单例的人都会调用

MyClass foo = MyClass::getInstance();
foo.doWork( blah blah );

并在他们的工作中使用单例。

根据定义(我认为)单例不是线程安全的,您注意到这一点是对的。添加线程安全是一个单独的问题。

看起来你的解决方案大部分时间都在那里。

【讨论】:

  • 单身的问题是我只能有一种货币。
  • 我知道我必须错过一些东西。 =)
【解决方案4】:

我不完全确定你想要做什么,但当然有单例模式,如果你想将实例数限制为 1 或者如果你想控制实例的创建方式,你可以使用工厂模式。

【讨论】:

  • 重点是我希望能够将实例数限制为 X,所以我不能使用直接的 Singleton 模式。这就是为什么我将其称为多吨模式:-)
【解决方案5】:

你已经拥有了你需要的一切!

编辑:

只需修改你抛出异常的部分:

if (it == map_.end()) {

    if ( -- part of allowed set -- ){
       -- create a new currency --
    }
    else {
       throw std::exception(("[Currency::Get] Currency '" + Code + "' not found").c_str());
    }
}

您可以通过多种方式进行比较,看看是否允许使用该货币:

  1. 另一个地图,列出允许的字符串代码
  2. 代码上的正则表达式比较
  3. 根据允许值的静态数组手动检查代码

EDIT2: 以下是与上述完全不同的答案:

根据您对问题的描述,您实际上可能想要考虑使用枚举而不是类来区分可能的货币类型。

enum Currency { US, CAN, JPY, CHF, EURO };

std::map<Currency curreny, std:string code>;

编辑 3:

添加类似:

private:
    std::vector<Currency> initializedList;

并且在您的 Get 方法中,不是返回“it->second”,而是返回对向量中对象的引用。如果它还不存在,首先将对象填充到向量中。

【讨论】:

  • 我希望将可能的不同货币限制在预先知道的某个集合中。
  • @Hazerider:然后在这个条件中添加一个条件。如果该货币不属于您的受限集,则抛出异常。否则,实例化新的!
  • 允许的货币集在“if (map_.empty()) {...}”块中初始化。在实现中,它进入数据库并加载所有活动货币。我不想在新货币激活时重新编译代码。我猜你可以称之为惰性初始化。
  • 好的,然后添加一个包含所有初始化货币的向量。查看我的编辑。
猜你喜欢
  • 1970-01-01
  • 2017-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-13
  • 2021-03-03
  • 2021-12-02
  • 1970-01-01
相关资源
最近更新 更多