C++ Object Slicing: Value Copies, Polymorphism, and Fixes
이 글의 핵심
Copying or passing derived objects by base value slices away derived members and breaks polymorphism; fix with references, pointers, and smart-pointer containers.
What is object slicing?
When you put a derived object into a base object by value, only the base subobject is copied; members and polymorphic behavior that belong only to Derived are cut off—like slicing a loaf of bread.
Why it happens (memory view)
In Base b = d;, the left-hand side is type Base, so the compiler only allocates sizeof(Base). The assignment copies the base part of d. Fields that exist only in Derived (e.g. y below) have no storage in b.
class Base {
int x; // part of Base layout
};
class Derived : public Base {
int y; // extra member after Base
};
Derived d;
d.x = 1;
d.y = 2;
Base b = d; // only x is meaningfully copied; y is sliced
// b’s static type is Base, so b.y is ill-formed
Why virtual dispatch “breaks” follows the same idea: by value, the live object is a base-sized copy, so calls may resolve like the base type (see example 1).
Causes
// 1. Pass by value
void func(Base b) { // slicing
// ...
}
Derived d;
func(d);
// 2. Return by value
Base func() {
Derived d;
return d; // slicing
}
// 3. Containers
std::vector<Base> vec;
Derived d;
vec.push_back(d); // slicing
Practical examples
Example 1: The bug
class Animal {
public:
virtual void speak() {
std::cout << "Animal" << std::endl;
}
};
class Dog : public Animal {
public:
void speak() override {
std::cout << "Woof!" << std::endl;
}
};
void makeSpeak(Animal a) { // by value
a.speak(); // always "Animal"
}
int main() {
Dog d;
makeSpeak(d); // "Animal" (slicing)
}
Example 2: Correct fix
// Reference
void makeSpeak(Animal& a) {
a.speak(); // polymorphism works
}
// Pointer
void makeSpeak(Animal* a) {
a->speak(); // polymorphism works
}
int main() {
Dog d;
makeSpeak(d); // "Woof!"
makeSpeak(&d); // "Woof!"
}
Example 3: Containers
// Bad: value container
std::vector<Animal> animals;
Dog d;
animals.push_back(d); // slicing
animals[0].speak(); // "Animal"
// Pointer container
std::vector<Animal*> animals;
Dog* d = new Dog();
animals.push_back(d);
animals[0]->speak(); // "Woof!"
// Smart pointers
std::vector<std::unique_ptr<Animal>> animals;
animals.push_back(std::make_unique<Dog>());
animals[0]->speak(); // "Woof!"
Example 4: Preventing copy
class Base {
public:
virtual ~Base() = default;
Base(const Base&) = delete;
Base& operator=(const Base&) = delete;
Base(Base&&) = default;
Base& operator=(Base&&) = default;
protected:
Base() = default;
};
Common pitfalls
Pitfall 1: Pass by value
// Bad
void process(Base obj) {
obj.virtualFunc(); // slicing
}
// Good
void process(const Base& obj) {
obj.virtualFunc(); // polymorphism
}
Pitfall 2: Return by value
// Bad
Base create() {
return Derived(); // slicing
}
// Good
std::unique_ptr<Base> create() {
return std::make_unique<Derived>();
}
Pitfall 3: Assignment
Derived d;
Base b;
b = d; // slicing
// Pointer keeps dynamic type
Base* b = &d; // polymorphism preserved
Pitfall 4: Storing in vectors by value
// Bad
std::vector<Base> vec;
vec.push_back(Derived()); // slicing
// Good
std::vector<std::unique_ptr<Base>> vec;
vec.push_back(std::make_unique<Derived>());
Detection
// Compiler warnings
g++ -Weffc++ program.cpp
// Delete copy ctor
class Base {
public:
Base() = default;
Base(const Base&) = delete; // prevent slicing
};
FAQ
Q1: When does slicing happen?
A: When a derived object is copied into a base by value.
Q2: Fixes?
A:
- References
- Pointers
- Smart pointers
Q3: How to detect?
A:
- Compiler warnings
- Deleted copy operations
Q4: Performance?
A: References and pointers add no slicing issue.
Q5: Containers?
A: Prefer smart-pointer containers for polymorphic types.
Q6: Learning resources?
A:
- Effective C++
- C++ Primer
- More Effective C++
Related posts (internal links)
- Inheritance and polymorphism
- Virtual functions
- Smart pointers
Practical tips
Debugging
- Check warnings first
- Reproduce minimally
Performance
- Profile before tuning
Code review
- Follow conventions
Practical checklist
Before coding
- Right technique for the problem?
- Team can maintain it?
- Meets performance goals?
While coding
- Warnings clean?
- Edge cases?
- Error handling?
At review
- Intent clear?
- Tests enough?
- Documented?
Keywords
C++, object slicing, polymorphism, inheritance, value semantics
Related posts
- Virtual functions
- CRTP pattern
- CRTP guide
- Decorator pattern
- Virtual destructor