I want to copy a vector of type Foo objects but the objects can be several different derived types of Foo. I can't figure out how to copy without slicing. Here's my toy code
#include "stdafx.h"
#include <memory>
#include <vector>
#include <string>
#include <iostream>
class Foo
{
public:
Foo() { m_x = "abc"; }
Foo( const Foo &other ) { m_x = other.m_x; }
virtual std::string ToString() { return m_x; }
std::string m_x;
};
class FooDerivedA : public Foo
{
public:
FooDerivedA() : Foo() { m_y = 123; }
std::string ToString() { return m_x + ", " + std::to_string( m_y ); }
int m_y;
};
class FooDerivedB : public Foo
{
public:
FooDerivedB() : Foo() { m_z = true; }
std::string ToString() { return m_x + ", " + std::to_string( m_z ); }
bool m_z;
};
class Foos
{
public:
Foos(){}
Foos( const Foos &other )
{
for ( auto &foo : other.m_Foos )
{
// I believe this is slicing. How can I prevent this?
auto f = std::unique_ptr<Foo>( new Foo( *foo ) );
m_Foos.push_back( std::move( f ) );
}
}
void Add( std::unique_ptr<Foo> foo ) { m_Foos.push_back( std::move( foo ) ); }
std::string ToString()
{
std::string s;
for ( auto &foo : m_Foos )
{
s += foo->ToString() + "\n";
}
return s;
}
private:
std::vector<std::unique_ptr<Foo>> m_Foos;
};
int main()
{
Foos f1;
f1.Add( std::unique_ptr<FooDerivedA>( new FooDerivedA ) );
auto f2 = Foos( f1 );
std::cout << "f1:" << f1.ToString() << std::endl;
std::cout << "f2:" << f2.ToString() << std::endl;
system("pause");
return 0;
}
I can't specify that the type should be FooDerivedA like:
auto f = std::unique_ptr<Foo>( new FooDerivedA( *foo ) );
because it might be FooDerivedB. How can I copy the data without slicing?
The classical method to solve this problem is to implement a virtual Foo *clone() const
, which is then called instead of the copy constructor.
So, if we have an object of some (derived form of) Foo
in x
, we can create another one by:
void someFunc(Foo *x)
{
Foo *copy_of_x = x->clone();
...
delete copy_of_x; // Important so we don't leak!
}
Note that since it's a virtual function, we can't call it in the constructor of foo
or any of it's derived types, as virtual functions don't operate "correctly" inside constructors.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With