본문으로 건너뛰기
Previous
Next
C++ Object Slicing: Value Copies, Polymorphism, and Fixes

C++ Object Slicing: Value Copies, Polymorphism, and Fixes

C++ Object Slicing: Value Copies, Polymorphism, and Fixes

이 글의 핵심

Object slicing in C++: copying derived objects into base values loses state and breaks virtual dispatch—use references, pointers, and smart pointers.

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++

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


같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.


이 글에서 다루는 키워드 (관련 검색어)

C++, Object Slicing, Polymorphism, Inheritance, Value Semantics 등으로 검색하시면 이 글이 도움이 됩니다.