0

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
Homer6
  • 15,034
  • 11
  • 61
  • 81
  • It's probably neither safe nor smart, but it is deterministic: The `Green` copy constructor copies the `Green` subobject of your `jerry` object, which has `q` set to `3`. – Kerrek SB Jul 23 '13 at 08:30
  • Do you know of a section of the standard that addresses this (ie. guarantees its determinism?) – Homer6 Jul 23 '13 at 08:31
  • no, it is not a safe practice, and people usually like to avoid slicing, because of problems it introduces. Although your program is well defined (see answers bellow) – BЈовић Jul 23 '13 at 08:42
  • It is not a safe practice leaving member variable public, which will be override(usually not what we what) silently(with no error or warning) with assignment. – lulyon Jul 23 '13 at 08:43
  • @BЈовић well it certainly seems that, even if it were safe, it would be unintuitive (and therefore a reason to be avoided) – Homer6 Jul 23 '13 at 08:43
  • 1
    C++11 4.10/3 (pointer conversion), I'd say. – Kerrek SB Jul 23 '13 at 08:48

1 Answers1

4

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

Casey
  • 41,449
  • 7
  • 95
  • 125
Stefano Falasca
  • 8,837
  • 2
  • 18
  • 24