I'm creating a MUD and am running into some design issues regarding Items. Items are stored in an ItemDatabase after being read from a JSON file. template <typename ItemType> static void load(std::string const& filename) { PropertyTree propertyTree; boost::property_tree::read_json(filename, propertyTree); for (auto& jsonItem : propertyTree) { Item *item = new ItemType(); item->fromJson(jsonItem); database_[item->id()] = item; } } At first, every Database::find() returned a shallow copy of the item, however, this quickly becomes dangerous and erroneous when items have a finite usage, e.g. a life potion that is consumed after one charge. Therefore, I want to start returning deep copies as std::unique_ptrs because each Item is unique and is possessed by only one entity. This avoids dangerous dangling pointers, if the Item is not maintained or destroyed by the caller: template <typename DataType> std::unique_ptr<DataType> EntityDatabase<DataType>::find(EntityId const& entityId) { auto found = std::find_if(std::begin(database_), std::end(database_), [&entityId](Container::value_type const& candidate) { return (candidate.second->id() == entityId); }); if (found != std::end(database_)) return std::unique_ptr<DataType>(new DataType(*found->second)); else return nullptr; } However, the resulting code is worse than hideous. Consider turning an `Item` into a `Weapon`: void Inventory::fromJson(PropertyTree::value_type const& properties) { auto jsonInventory = *properties.second.find("Inventory"); auto weaponId = getValue<EntityId>(jsonInventory, "Weapon"); weapon_.reset(static_cast<Weapon*>(ItemDatabase::find(weaponId).release())); auto armorId = getValue<EntityId>(jsonInventory, "Armor"); armor_.reset(static_cast<Armor*>(ItemDatabase::find(armorId).release())); money_ = getValue<Money>(jsonInventory, "Money"); for (auto& item : jsonInventory.second.get_child("Items")) { auto itemId = boost::lexical_cast<EntityId, std::string>(item.second.data()); inventory_.push_back(std::move(ItemDatabase::find(itemId))); } } How can I improve the code or the design? Edit: Some of the assumptions made by Yuushi are correct and I wish to state them here: EntityDatabase is the base class for all Databases such as ItemDatabase. It contains raw pointers to objects created after reading the appropriate Json-File. template <typename DataType> class EntityDatabase { public: typedef std::map<EntityId, DataType*> Container; Loading happens in the derived Databases, for example the loading of Items including Armor, Weapons, Consumables: class ItemDatabase : public EntityDatabase<Item> { public: static void load(); template <typename ItemType> static void load(std::string const& filename) { PropertyTree propertyTree; boost::property_tree::read_json(filename, propertyTree); for (auto& jsonItem : propertyTree) { Item *item = new ItemType(); item->fromJson(jsonItem); database_[item->id()] = item; } } Further, Weapon, Armor, Consumable are all of type `Item`: class Item { Item(); virtual ~Item(); //.... } class Weapon : public Item { //... } class Armor : public Item { //... } ItemDatabase::find() returns a `unique_ptr<Item>`, a deep copy of the pointer object to be owned by only one person: static std::unique_ptr<Item> find(EntityId const& id) { auto item = database_.find(id); if (item != std::end(database_)) return std::unique_ptr<Item>(new Item(**item)); else return nullptr; } The Inventory class I showed whose `weapon_` and `armor_` fields must be set looks like this: class Inventory : public JsonObject { public: Inventory(); ~Inventory(); virtual void fromJson(PropertyTree::value_type const& properties) override; virtual void toJson(PropertyTree& propertyTree); void equipWeapon(Weapon *weapon); void removeWeapon(); std::shared_ptr<Weapon> weapon() const; void equipArmor(Armor *armor); void removeArmor(); std::shared_ptr<Armor> armor() const; void pickUpItem(Item *item); std::shared_ptr<Item> dropItem(Item *item); void destroyItem(Item *item); std::shared_ptr<Item> getItem(Item *item); std::shared_ptr<Item> findItem(std::string const& name); bool hasItem(Item *item); void earnMoney(Money amount); void spendMoney(Money amount); Money money() const; size_t size() const; friend std::ostream& operator<<(std::ostream& os, Inventory const& inventory); private: bool isBeingUsedAsAWeapon(Item *item) const; bool isBeingUsedAsArmor(Item *item) const; private: std::shared_ptr<Weapon> weapon_; std::shared_ptr<Armor> armor_; std::vector<std::shared_ptr<Item>> inventory_; Money money_; }; Wherein an Item only of type Weapon can be equipped as a weapon, but both Weapon and Armor must be displayed and kept in the inventory.