0

Lets say I have the following data structures:

struct Base
{
    Base(const int id, const std::string &name, const std::string &category):
        id(id), name(name), category(category) {}

    int         id;
    std::string name;
    std::string category;
};

struct A : public Base
{
    A(const int id, const std::string &name, const std::string &category,
      const int x, const int y) :
     Base(id, name, category), x(x), y(y) {}
    int x, y;
};

I want to create a single factory method that returns a vector of the derived classes, where the id, name, and category is known in the function. The problem that I run into is slicing...

std::vector< Base* > getVector(...)

Data members of struct A are lost! (dynamic_cast back to A acceptable in production code?)

So I have this template method, but I still don't think its the best solution:

template< class T >
std::vector< T > getVector()
{
    std::vector< T > retVal;

    retVal.push_back(T(45, "The Matrix", "Science Fiction"));
    retVal.push_back(T(45, "Good Luck Chuck", "Comedy"));
    ...
    return retVal;
}

Is there any better solution other than the template method?

rem45acp
  • 469
  • 5
  • 16
  • 1
    How does your `std::vector getVector(...)` exhibit slicing? Will your members be of varying derived types? – Joseph Mansfield Feb 18 '13 at 22:39
  • Your description of the problem ("The problem I run into is slicing") is incomplete. Please post a **short**, **complete** program that demonstrates your problem. See http://sscce.org/. – Robᵩ Feb 18 '13 at 22:44
  • A `Base*` is the same size as a `A*`. The pointer will not get sliced. – Drew Dormann Feb 18 '13 at 22:48
  • 2
    you should have virtual destructor for `Base` btw. – billz Feb 18 '13 at 22:49
  • It turns out I have my design completely wrong. What I wanted was to not have to declare a vector for each type of derived object. The template method would simply create the vector of what ever derived object is needed as long as the first three parameters are the same. Concerning slicing, if I simply inserted an A object into a std::vector, the x and y members of A are inaccessible unless the cast is performed (which I learned should be avoided). – rem45acp Feb 18 '13 at 23:07

2 Answers2

1

What you are asking for seems questionable, because you want:

  • To abstract object creation until runtime
  • To access specific members after object creation at compile time

So there is no way to make it work decently. Your template code still needs to statically know the type of your object at caller site, so the object creation is not abstracted away.

My advice is to think again about your issue: why do you want to have a dynamic factory? Why can't all the objects share accessors in the Base class?

If you can't answer clearly to these two questions, it may mean you should not have this class hierarchy in the first place.

Qortex
  • 7,087
  • 3
  • 42
  • 59
1

I actually think your template is probably the best solution, at least it is idiomatic C++.

Using RTTI such as dynamic_cast is also ok, but it's rather less safe (runtime vs. compiletime type checking) and often less efficient. However, sometimes – or quite often in fact – you need to have the polymorphism decided at runtime only (e.g. when you need different derived objects in the same std::vector and can't use a fixed-length std::tuple). Then you can, like you already prepared, get around the slicing issues by using a vector of base-class pointers rather than objects. The problem with (plain) pointers is that they somewhat circumvent C++' automatic memory management: when an std::vector<Base*> goes out of scope, the pointed-to derived objects are not deleted by stay hanging around somewhere inaccessible: you have a memory leak. That's why languages like Java, which relies much more on this way of using inheritance, are garbage-collected. This problem is discussed elsewhere, the recommended solution is to replace Base* with std::unique_ptr<Base>.

Once you have this running you can then make use of runtime-polymorphic functions. You should normally not need dynamic_cast, but rather write everything where different derived instances differ as virtual member functions.

Community
  • 1
  • 1
leftaroundabout
  • 117,950
  • 5
  • 174
  • 319