Question

I have a case in my code where I'd like to use object slicing, but I'm trying to determine if it's safe or smart to do it. In trying to determine this, I ran the following example:

#include <iostream>

using namespace std;


class Dog{

    public:
        Dog( int x )
            :x{x}
        {

        };

        int x;

};


class Spaniel: public Dog{

    public:
        Spaniel( int x, int y )
            :Dog{x}, y{y}
        {

        }

        int y;

};


class Green{

    public:
     Green( int q )
         :q{q}
     {

     }

     int q;

};


class GreenSpaniel: public Spaniel, public Green{

   public:
        GreenSpaniel( int x, int y, int q, int z )
            :Spaniel{x,y}, Green{q}, z{z}
        {

        }

        int z;

};



int main(){

    GreenSpaniel jerry{ 1,2,3,4 };

    Green fred = jerry;

    cout << fred.q << endl;  //correctly displays "3"

    return 0;

}

I was expecting it to return 1 because the base class was not the top-most (root), but it displays 3. So, my question is why/how is it showing the correct answer and is this a safe practice? How would your answer change if any of the classes had virtual tables? If you don't consider it safe, do you have any workarounds for copying a non-root base object from a derived object?

I ran this in linux under gcc 4.6.3 with the following command:

g++ -std=c++0x main.cc
Was it helpful?

Solution

What is happening is that fred is being constructed using a compiler-synthesized copy constructor, which takes const Green& as an argument. It performs a shallow copy of the content of the Green portion of jerry.

you would have seen the same result if you used

const Green& fred = jerry;

where no copy is being carried out, you are just accessing the jerry portion of jerry, by giving it the name fred.

As for the other part of the question, there is nothing un-safe about your design, just it's "complex" and you need to be aware of what's happening. You might want to read this and related pages for some discussion on the topic.

Of course you can define your own copy constructor(s)/operator(s) having different behaviour. Or you can just inhibit the compiler to generate them

The traditional way to deal with these is to declare a private copy constructor and copy assignment, and then document why this is done. A new alternative was introduced in C++2011, declaring a copy constructor and a copy assignment operator, but marking both as deleted. Deriving from noncopyable is simpler and clearer, and doesn't require additional documentation.

source

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top