What is a possible workaround for object slicing in c++?
You need to store pointers. If these refer to dynamically allocated objects, use smart pointers.
Avoiding object slicing
The fundamental issue is copying an object (which is not an issue in languages where classes are "reference types", but in C++ the default is to pass things by value, i.e. making a copy). "Slicing" means copying the value of a bigger object (of type B
, which derives from A
) into a smaller object (of type A
). Because A
is smaller, only a partial copy is made.
When you create a container, its elements are full objects of their own. For example:
std::vector<int> v(3); // define a vector of 3 integers
int i = 42;
v[0] = i; // copy 42 into v[0]
v[0]
is an int
variable, just like i
.
The same thing happens with classes:
class Base { ... };
std::vector<Base> v(3); // instantiates 3 Base objects
Base x(42);
v[0] = x;
The last line copies the contents of the x
object into the v[0]
object.
If we change the type of x
like this:
class Derived : public Base { ... };
std::vector<Base> v(3);
Derived x(42, "hello");
v[0] = x;
... then v[0] = x
tries to copy the contents of a Derived
object into a Base
object. What happens in this case is that all members declared in Derived
are ignored. Only the data members declared in the base class Base
are copied, because that's all v[0]
has room for.
What a pointer gives you is the ability to avoid copying. When you do
T x;
T *ptr = &x;
, ptr
is not a copy of x
, it just points to x
.
Similarly, you can do
Derived obj;
Base *ptr = &obj;
&obj
and ptr
have different types (Derived *
and Base *
, respectively), but C++ allows this code anyway. Because Derived
objects contain all members of Base
, it's OK to let a Base
pointer point at a Derived
instance.
What this gives you is essentially a reduced interface to obj
. When accessed through ptr
, it only has the methods declared in Base
. But because no copying was done, all data (including the Derived
specific parts) are still there and can be used internally.
As for virtual
: Normally, when you call a method foo
through an object of type Base
, it will invoke exactly Base::foo
(i.e. the method defined in Base
). This happens even if the call is made through a pointer that actually points at a derived object (as described above) with a different implementation of the method:
class Base {
public:
void foo() const { std::cout << "hello from Base::foo\n"; }
};
class Derived : public Base {
public:
void foo() const { std::cout << "hello from Derived::foo\n"; }
};
Derived obj;
Base *ptr = &obj;
obj.foo(); // calls Derived::foo
ptr->foo(); // calls Base::foo, even though ptr actually points to a Derived object
By marking foo
as virtual
, we force the method call to use the actual type of the object, instead of the declared type of the pointer the call is made through:
class Base {
public:
virtual void foo() const { std::cout << "hello from Base::foo\n"; }
};
class Derived : public Base {
public:
void foo() const { std::cout << "hello from Derived::foo\n"; }
};
Derived obj;
Base *ptr = &obj;
obj.foo(); // calls Derived::foo
ptr->foo(); // also calls Derived::foo
virtual
has no effect on normal objects because there the declared type and the actual type are always the same. It only affects method calls made through pointers (and references) to objects, because those have the ability to refer to other objects (of potentially different types).
And that is another reason to store a collection of pointers: When you have several different subclasses of GameObject
, all of which implement their own custom draw
method, you want the code to pay attention to the actual types of the objects, so the right method gets called in each case. If draw
weren't virtual, your code would attempt to invoke GameObject::draw
, which doesn't exist. Depending on how exactly you code it, this either wouldn't compile in the first place or abort at runtime.
A potential workaround for C++ slicing?
_foo
is a FOO&
.&_foo
is a FOO*
.
The compiler cannot convert from 'FOO *' to 'FOO &'
.
what you want is ..., _realFoo(_foo)
Unrelated: What you probably actually want is a std::unique_ptr<FOO>
member instead. This will be much smaller, and much less error prone.
The struct as you have it now, contains a full and complete instance of FOO
, and a full and complete instance of FOOSUBCLASS
, and a reference. At least put the two FOO
thingies in a union, so the size of BAR
is only slightly bigger than the biggest FOO
derivative. That would probably use the same memory as a unique_ptr<FOO>
. Unfortunately, union
s are a common source of bugs in C and C++.
Another problem is if someone comes along and writes
class FOOSOMETHINGELSE : public FOO {
int buffer[1024];
};
Then you'll have to go and find the BAR
class and change it and make it even bigger, and recompile all of your code that uses BAR
everywhere. Whereas, if you used a unique_ptr<FOO>
, then you won't have to change BAR
or recompile anything. So smaller chance of error.
Member variables and object slicing
Changing the classes
The class definitions for Base
and Derived
should be changed to:
class Base {
public:
int x = 1;
virtual void f() { }
};
class Derived : public Base {
public:
Derived() : Base() { x = 2; }
int y = 55;
void f() { }
};
The notable addition is the virtual function void f()
to allow dynamic_cast
to handle casting the pointers.
Using unique_ptr
Unique pointers can be used to store, retrieve, modify and safely delete pointers to Derived
objects residing within a vector of pointers to Base
objects. The example below uses heap-allocated objects.
/* Insertion */
// Create a vector of unique_ptr to Base and add a unique_ptr to Derived to it
std::vector<std::unique_ptr<Base>> v;
std::unique_ptr<Derived> p1(new Derived());
v.push_back(std::move(p1));
/* Retrieval */
// Release the pointer at base to place it into p2
std::unique_ptr<Derived> p2(dynamic_cast<Derived*>(v[0].release()));
/* Modification */
p2->x = 0xff; // Modify x (Decimal 255)
p2->y = 0xffff; // Modify y (Decimal 65535)
int y = p2->y; // Copy the value of the Derived object's y to an int for use later
// Move the pointer to the modified object back to v
v[0] = std::move(p2);
/* Output */
std::cout << v[0]->x << std::endl; // Outputs 255
std::cout << y << std::endl; // OUtputs 65535
/* Delete */
// To safely delete the object, it must be treated as a pointer to a Derived object
// Thus it must be released from the control of v and deleted via p3, which is cast in the same manner as p2 was
std::unique_ptr<Derived> p3(dynamic_cast<Derived*>(v[0].release()));
p3.reset(); // This deletes the object as well, not just the reference to it!
Functions as expected and outputs 255
and 65535
.
Object slicing : pass Derived as Base by value - safe or dangerous?
Resharper acts in a contradict way from my belief.
it acts in a correct way, lets see:
Warning when the type is value.
this is for this code:
class B1{ int field1=0; };
class B2{ int field2=0; };
class C: public B1,public B2{ };
class Test{
void f(B1 b){ }
void f2(){
C c;
f(c);
}
};
in a function call, value initialization of variable b
of type B1
from variable c
of type C
which is derived from B1
, will execute a copy constructor of B1
whose signature is B1(const B1& rhs);
, in this constructor (whether it is auto generated or not) only fields present in B1
will be copied from rhs
, so the rest (part from B2
or C
) will get sliced.
No warning when the type is reference (change #2 from B1 to B1&).
void f(B1& b){ }
that is correct, this is how polymorphic objects should be passed around, by assigning objects to pointers or references of base class types.
Always no warning when C derived from only B1, no matter #2 is. (B or B&)
thats only because C
has no fields, so there is no slicing in this case. Try adding int n;
to C
and warning will be shown again.
C++: Is there a more elegant solution to this (multiple dispatch) runtime polymorphism?
Here is a simplified example (untested) of the classical double dispatch.
struct Circle;
struct Rectangle;
struct Shape {
virtual bool intersect (const Shape&) const = 0;
virtual bool intersectWith (const Circle&) const = 0;
virtual bool intersectWith (const Rectangle&) const = 0;
};
struct Circle : Shape {
bool intersect (const Shape& other) const override {
return other.intersectWith(*this);
}
bool intersectWith (const Circle& other) const override {
return /* circle x circle intersect code */;
}
bool intersectWith (const Rectangle& other) const override {
return /* circle x rectangle intersect code*/;
}
};
struct Rectangle : Shape {
bool intersect (const Shape& other) const override {
return other.intersectWith(*this);
}
bool intersectWith (const Circle& other) const override {
return /* rectangle x circle intersect code */;
}
bool intersectWith (const Rectangle& other) const override {
return /* rectangle x rectangle intersect code*/;
}
};
As you can see you weren't too far off.
Notes:
return intersectWith(*this);
needs to be repeated in each derived class. The text of the method is the same every time, but the type ofthis
is different. This can be templatized to avoid repetition, but it probably isn't worth it.- The
Shape
base class (and, naturally, each of its derived classes) needs to know about allShape
-derived classes. This creates a cyclic dependency between classes. There are ways to avoid it but these do require casting.
This is not the solution of the multiple-dispatch problem, but it is a solution. A variant-based solution may or may not be preferable, depending on what other stuff you have in your code.
Object slicing when using std::enable_if
@Sebastian's answer explains the problem, but the suggested solution is going to be problematic: you can't specialize a base class template on properties of the derived class using CRTP, since the derived class isn't complete when the base class is instantiated. I would suggest that you instead always override f1
in A
, and use tag dispatching to determine whether to dispatch to f2
in the derived class or the default implementation in Base
:
template<typename Derived>
class A : public Base
{
double f1_impl(boost::mpl::true_, double x, double) const
{
std::cout << "Called Derived Method\n";
return static_cast<const Derived*>(this)->f2(x);
}
double f1_impl(boost::mpl::false_, double x, double y) const
{
return Base::f1(x, y);
}
public:
double f1(double x, double y) const override
{
using has_f2 = typename has_member_function_f2
< Derived
, double
, boost::mpl::list<double>
, boost::function_types::const_qualified
>::type;
return f1_impl(has_f2{}, x, y);
}
};
DEMO
Curious case of C++ Object slicing behaviour on Base class static reference data member
When you do
Base::base=Derived::derived;
You are not setting the base reference to refer to the derived class. This is the assignment operator and all it does is asign the Base
part of derived
to base
.
base
is still of type Base
and can never change as references can only ever be initialized once and you do that with
Base& Base::base=Base::getBaseInstance();
If you want this reassignment behavior you are going to need to use a pointer type.
Pass by reference avoids object slicing
The derived object gets "sliced" when it is used to instantiate a base class object. This happens when you pass by value, because the function parameter is a base class object, not anything else. It is the equivalent of doing this:
Derived d;
Base b = d; // b is a Base, not a Derived. It knows nothing of Derived.
A reference is simply an alias for an object, so a reference to a Base
object does not involve the construction of a new Base
object. It simply aliases one:
Base& b = d; // b aliases d, i.e. a Derived object
After the above statement, b
is an alias for d
and can be use to access d
's Base
interface polymorphically. It can alias a Derived
object because Derived
is-a Base
. This wouldn't be possible with, say, private inheritance.
Related Topics
Execute Rdmsr and Wrmsr Instructions from C/C++ Code
Is There a Default Hash Function for an Unordered_Set of a Custom Class
Why Derive from a Concrete Class Is a Poor Design
How to Get Size of Check and Gap in Check Box
Calculate Md5 of a String in C++
How to Traverse a Boost::Multi_Array
Check If Two Types Are Equal in C++
Overloading Postfix and Prefix Operators
How to Add Playable(Such as Wav,Wmv) Header with Pcm Data/Buffer in iOS
C++ Access to SQL Server from Linux
Does Insertion to Stl Map Invalidate Other Existing Iterator
Multithreading on Dual Core MAChine
Get Functions Names in a Shared Library Programmatically