Michael Blake
Michael Blake

Reputation: 995

Avoiding object slicing in a method call

I've been trying to rewrite a program of mine to allow for inherited classes. I'm running into a problem with slicing. I originally had Parent m_parent in my Test class (therefore no dynamic allocation in Test), but added that thinking it'd fix my issue, but it hasn't. I don't know if I'm making my issue worse or if it's as simple as writing a copy constructor (or some other simple fix).

class Parent
{
    public:
        Parent();
    private:
        string a;
};
class Child : public Parent
{
     private:
        string b;
};
class Test
{
     public:
        Test()
        {
            m_parent = new Parent;
        }
        void testFunction(Parent &parent)
        {
            *m_parent = parent;
        }
        ~Test()
        {
             delete m_parent;
        }
     private:
        Parent * m_parent;
};

Doing something like this still causes slicing...

Child c("foo", "bar");
Parent * p = &c;
Test.testFunction(*p);

Upvotes: 1

Views: 497

Answers (2)

David Haim
David Haim

Reputation: 26546

Yes. this line:

*m_parent = parent;

causes the object to be sliced into Parent object regardless on what it really is.

Other than that, there are many error proned lines of code in your example, but I guess its only because it's a very specific question.

Edit: A suggestion how to avoid slicing:

template <class T>
void testFunction(T& t){
    static_assert(std::is_base_of<Parent,T>::value,"only subclasses of Parent may be passed to testFunction");
     delete m_parent;
     m_parent = new T(t); //or: new T(std::move(t));
}

EDIT 2: this shall work if you pass a child object as real object and not as a parent pointer , AKA testFunction(myChild)

EDIT3: @Jarod42 has a good point about the clone method. the two of the solution can be used together

Upvotes: 2

Jarod42
Jarod42

Reputation: 218323

There are no object slicings, but m_parent doesn't become a Child and only string a is copied.

If you want keep a copy in Test, I suggest using Clone:

class Parent
{
public:
    virtual ~Parent() = default;
    virtual Parent* Clone() const { return new Parent(*this); }
private:
    string a;
};

class Child : public Parent
{
public:
    Child* Clone() const override { return new Child(*this); }
private:
    string b;
};

class Test
{
public:
    void testFunction(const Parent &parent)
    {
        m_parent.reset(parent.Clone());
    }

private:
    std::unique_ptr<Parent> m_parent;
};

Upvotes: 2

Related Questions