Link to home
Start Free TrialLog in
Avatar of ambuli
ambuliFlag for United States of America

asked on

Copying a vector of derived classes to a vector of base class

Hi there,

I have a vector of derived classes and I want to make a vector of base class.

From vector<DerivedClass*> to vector<BaseClass*>

How can I do this?
ASKER CERTIFIED SOLUTION
Avatar of pepr
pepr

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
What's wrong with just using the constructor?

#include <vector>

struct B
{
   virtual ~B(){}
};

struct D : B
{
};

int main()
{
   auto && vD = std::vector<D *>{ new D(), new D(), new D() };
   auto && vB = std::vector<B *>(vD.begin(), vD.end());
}

Open in new window


BTW: it is a patently bad idea to use raw pointers in an STL container. It is wholly unsafe because you have to manage the lifetime of each and every item. You should consider using a smart pointer, such as std::unique_ptr or std::shared_ptr.

#include <vector>
#include <memory>

struct B
{
   virtual ~B(){}
};

struct D : B
{
};

int main()
{
   auto && vD = std::vector<std::shared_ptr<D>>{
      std::shared_ptr<D>(new D()),
      std::shared_ptr<D>(new D()),
      std::shared_ptr<D>(new D())
   };
   auto && vB = std::vector<std::shared_ptr<B>>(vD.begin(), vD.end());
}

Open in new window

it is a patently bad idea to use raw pointers in an STL container
actually, if you want to store baseclass pointers of multiple derived classes for virtual use in a container it is the only way ...

you could make the thing rather safe by using an 'all' container which contains all those baseclass pointers (or objects). then the owner of this container, typically a singleton or the baseclass itself using a static container, is solely responsible for managing (adding and removing) the pointers.

I have a vector of derived classes and I want to make a vector of base class.
From vector<DerivedClass*> to vector<BaseClass*>
if we assume that all classes were derived public from base class and the base class in question is not second within a multiple inheritance, then base class pointer and derived class pointer have same pointer value, what means that you could cast the vector from vector<DerivedClass*> to vector<BaseClass*>

std::vector<Derived*> darr;
...
std::vector<Base*> barr = (std::vector<Base*>)darr;

Open in new window


this code would work but actually it is much easier if you would not use std::vector<Derived*> at all but always std::vector<Base*> if you have the need to store pointers of multiple derived classes. or use a vector with derived objects rather than pointers. each of these objects may be used with full derived functionality and base class functionality:

std::vector<Derived> darr;
darr.push_back(Derived());
Derived & d = darr.back();
d.Base::someBaseFunction();
d.someVirtualFunction();
...
// you even could do (not recommended)
Base * pb = &darr[index];
// here the owner of barr must be absolutely aware that 
// the objects would be deleted with the destruction of darr.
std::vector<Base*> barr;
barr.push_back(pb);

Open in new window


note, if you can't make sure that there is exactly one which is responsible for the pointer and its deletion, you should use smart pointers as shown by evilrix. though smart pointers to different derived classes couldn't be put into one container, you often will see that this isn't actually necessary.

Sara
>> actually, if you want to store baseclass pointers of multiple derived classes for virtual use in a container it is the only way ...

Sorry, are you saying that you can't use a shared_ptr to represent both a derived and base class pointer to the same object?

There is absolutely no reason why you can't use two shared pointers with a shared reference count to both a base and derived object. This is perfectly safe and will completely manage ownership semantics. You do not need to re-invent the wheel by creating a singleton "all" container to manage ownership.

In other words this is perfectly safe...

#include <vector>
#include <iostream>
#include <memory>

struct B
{
   virtual ~B(){}

   virtual void foo() const = 0;
};

struct D : B
{
   void foo() const
   {
      std::cout << "D::foo()" << std::endl;
   }
};


int main()
{
   auto && vD = std::vector<std::shared_ptr<D>>{
      std::shared_ptr<D>(new D()),
      std::shared_ptr<D>(new D()),
      std::shared_ptr<D>(new D())
   };
   
   auto && vB = std::vector<std::shared_ptr<B>>(vD.begin(), vD.end());

   for(auto const & pB : vB)
   {
      pB->foo();
   }
}

Open in new window


Or, is it possible I've just misunderstood what you are asserting?
Avatar of pepr
pepr

My +1 to evilrix code -- using the vector constructor, passing the source begin(), end() iterators.

The shared_ptr is the enhancement that is orthogonal to the problem. It is one way how to solve the destruction of the objects in the vector when they are not used any more. (In my example, the objects were never destroyed, and they were released only when the runtime released the heap space used by the process -- I should have emphasize that more in my comment.
are you saying that you can't use a shared_ptr to represent both a derived and base class pointer to the same object?
no. i said that a container to store objects of different derived classes which were derived from the same base class could only be made by using base class pointers (raw pointers). i also said, that it is not really necessary to copy pointers stored in a vector when the derived pointers have the same pointer value as the base class pointers (what is normally the case if multiple inheritance is not involved).

a shared pointer solves the problem to free an object where multiple pointers to the same instance may exist. in times where we have languages with automatic garbage collection (gc), this add same comfort to older languages without a gc. however, the task also could be solved by a solid design where there is one owner which lives longer than all other who would access an object and therefore can safely free the object. granted, that such a solid design exists, it is absolutely no issue to have a container that stores base class pointers for virtual use.

Sara
>>  could only be made by using base class pointers (raw pointers)
Okay, but they don't have to be raw pointers, they can still be shared pointers, right?

>> where there is one owner which lives longer than all other who would access an object
That's true if it is part of the natural design, but I wouldn't specifically design that as a solution since shared pointers also solve the problem and they are part of the language.

Okay, good. It seems are are more of less on the same page. I just wanted to seek clarification (a) to make sure I'd not missed something important and (b) because if I was unsure there was a good chance so the was the asker.

Thanks for taking the time to clear that up, Sarah.
Okay, but they don't have to be raw pointers, they can still be shared pointers, right?
i never tried like

std::vector< shared_ptr< Base > > somearr;

and would have thought it doesn't work for abstract base classes?

It seems we are more or less on the same page.
yes. generally pointers should be avoided and if you do so, it doesn't make so much difference whether they would have been shared pointers or not, right? function pointers and base class pointers for virtual use can make some exception to this general rule, at least when using c++ 98 standard where you have little alternatives.

Sara
You are perfectly safe to use shared_ptr. After all, under the hood it is still just a pointer.