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:
- Another map, listing allowed String codes
- A regular expression comparison on the code
- 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.
精彩评论