开发者

C++ Unique Objects

开发者 https://www.devze.com 2023-02-19 08:05 出处:网络
I would like to create a class where the possible universe of instances is limited, and of which users cannot create new instances. For example, currencies are unique, and users of the library I am wo

I would like to create a class where the possible universe of instances is limited, and of which users cannot create new instances. For example, currencies are unique, and users of the library I am working should not be able to create new currencies, or copy existing ones. It is kind of like a multiple-ton pattern, I guess. What I have so far is:

#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 da开发者_运维知识库tabase 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;
}

Are there any obvious problems with this design? (I know that this isn't thread-safe) Is there a generally accepted technique/pattern that I am not aware of that is traditionally used to achieve this?

Thanks, Marc.


Look into boost:noncopyable to prevent copying; that's a good reference solution that you can both trust and learn from. Preventing instantiation is easy: a private constructor.


I am not fully sure, what you want to do, but of course there is the singleton pattern, if you want to limit the number of instances to 1 or if you want to control how the instances are created you could use the factory pattern.


Declare a private copy constructor Currency::Currency(const Currency&), but don't define it. This prevents initialising a Currency object with another Currency object. Similarly, declare a private assignement operator Currency& Currency::operator=(const Currency&). That way you prevent assigning one currency to another currency.


What you're talking about, I believe, is the Singleton pattern. So you can read up on that.

The way in most languages to get a singleton is to

1) make your constructors private

2) instantiate a single instance (the singleton) of your class internal to the class (private static)

3) create a static (class) method that returns a pointer to the static private instance created in step #2 (typically called getInstance() or instance() or something like that.)

Anyone needing to use this singleton then calls

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

and uses the singleton for their work.

Singletons aren't by definition (I think) thread safe, and you're right to notice that. Adding thread safety is a separate issue.

Looks like you were most of the way there with your solution.


You already have everything you need!

EDIT:

Just modify the part where you throw your exception:

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());
    }
}

You can do your comparison to see if the currency is allowed in a variety of ways:

  1. Another map, listing allowed String codes
  2. A regular expression comparison on the code
  3. Manually checking the code against a static array of allowed values

EDIT2: Below is a completely separate answer from the one above:

From the description of your problem, you might actually want to consider using enums rather than classes to differentiate between the possible currency types.

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

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

EDIT 3:

Add something like:

private:
    std::vector<Currency> initializedList;

And in your Get method, rather than return "it->second", return a reference to an object in the vector. If it doesn't yet exist, first populate the object into the vector.

0

精彩评论

暂无评论...
验证码 换一张
取 消