how makes this work ?
I want to use a vector of multiple types (research, add, delete) for an inventory management (Potions, Weapons, etc.. all derived from virtual class Item).
I simplify the problem here : I have a vector containing Item (Base class) and Weapons (Derived class). For memory management issues, i prefered using unique_ptr but didn't a way to return it or use it properly.
Example Below :
// UniquePointerAndPolymorphism.cpp : Ce fichier contient la fonction 'main'. L'exécution du programme commence et se termine à cet endroit.
//
#include <iostream>
#include <vector>
class Item
{
protected:
std::string name_;
public:
Item(std::string name) :name_(name) {};
virtual std::string getName() { return "b_" name_; };
};
class Weapon : public Item
{
public:
Weapon(std::string name) : Item(name) {};
std::string getName() override { return "d_" name_; };
};
std::vector<std::unique_ptr<Item>> elements;
std::unique_ptr<Weapon> getAnElement_uniquePointer(int i)
{
/*
*
* How to return unique pointer from the vector ?????
*
*/
return std::make_unique<Weapon>("returned");
}
Weapon* getAnElement_rawPointer(int i)
{
if (auto oneElement = dynamic_cast<Weapon*>(elements[i].get()))
{
return oneElement;
}
else
{
return nullptr;
}
}
int main()
{
elements.push_back(std::make_unique<Weapon>("1"));
elements.push_back(std::make_unique<Item>("2"));
elements.push_back(std::make_unique<Weapon>("3"));
Weapon* rptElement = getAnElement_rawPointer(2);
std::cout << rptElement->getName() << std::endl;
std::unique_ptr<Weapon> uptElement = std::move(getAnElement_uniquePointer(0));
std::cout << uptElement->getName() << std::endl;
}
So I have a few questions :
- Even if it seems to not be the problem, is Polymorphism compatible with smart pointers ?
- Was returning a raw pointer the only solution ?
- Do I have to use shared_pointer to use it in the vector and in another part of my programm ?
- Is there a way to return a reference ?
Thanks. Sebastien
CodePudding user response:
Even if it seems to not be the problem, is Polymorphism compatible with smart pointers ?
Yes, polymorphism is compatible with smart pointers.
Was returning a raw pointer the only solution ?
No, returning a raw pointer is not the only solution. (See optional reference answer below.)
One convention is to use raw pointers to indicate a non-owning pointer.
Unfortunately, it isn't often clear that the intent is for raw pointers to be non-owning pointers, because historically raw pointers are often owning pointers.
You would need to adhere to a discipline of raw pointers are non-owning pointers if you adopt such a policy. (I think it is a good policy, and I use it.)
Do I have to use shared_pointer to use it in the vector and in another part of my program?
No, you do not have to use a shared_ptr. Using a shared_ptr is federated ownership.
Federated ownership is tantamount to being a global variable. Unless the object is immutable, reasoning about the object state is tricky, especially if different owners have different invariants.
I consider using a shared_ptr a last resort. My go to smart pointer is a unique_ptr (and raw pointers as non-owning).
Is there a way to return a reference?
Yes, you could return a reference to a Weapon.
Weapon& getAnElement_reference(int i) {
if (auto oneElement = dynamic_cast<Weapon*>(elements[i].get()))
{
return *oneElement;
}
throw std::logic_error("No weapon");
}
But what to do if the index refers to an object that is not a Weapon? The code would need to throw an exception.
An alternative to throwing an exception is to return a std::optional<std::reference_wrapper<Weapon>>
which could be a std::nullopt_t
when there is no Weapon at that index. Then the caller needs to access the optional explicitly.
That would also be safer than a raw pointer if the caller neglects to check for nullptr
, which is an easy mistake to make and the compiler won't help catch those mistakes.
CodePudding user response:
A unique pointer is exactly that: unique. There is only supposed to be one unique pointer to each object. As soon as you try to return the unique pointer, there would have to be 2: one in your vector elements and one returned from your function. There are ways around this I think, like returning a reference to the pointer, but that gets messy quickly.
You can create a shared_ptr from a unique_ptr, or you can just use a shared_ptr in your vector to begin with.
Alternatively, in the case of an inventory system you're working with relatively simple objects. In my experience you'd want one class for the items in the actual game world, and a separate InventoryItem class for items that are stored in an inventory.
Each item then would store the type of inventoryItem it constructs. This not only means you don't have to mess with all the ins and outs of making sure inventory items don't show up in your gameworld, it also means you can easily pass those items by value if it's more convenient, because the amount of actual data in such an item is laughably small :)
At the end of the day it's really a personal choice how to manage your pointers, or if you should use pointers at all. Only a unique_ptr wouldn't be used according to its purpose here.