49. 공변 반환 타입과 반환 포인터 조정(covariant return / pointer adjustment)

난이도 중 해설 보기 →

결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커 (A)(B) 는 주목 위치 힌트다.

결함 코드 · C++
// 주제: 가상 함수의 공변 반환 타입(covariant return type)과, 반환 포인터의
//       런타임 조정(pointer adjustment) — 반환 타입이 다중 상속으로 base 서브오브젝트가
//       오프셋 0이 아닐 때 vtable 썽크가 반환 포인터를 조정한다.
//
// 과제:
//   1) 다음 각 줄의 표준출력을 정확히 예측하라.
//   2) Factory* 를 통해 make() 를 부르면 반환형이 RetBase* 인데, 실제 구현은
//      RetDerived* 를 돌려준다. 이때 반환된 포인터 값이 어떻게 조정되는지 설명하라.
//   3) L2 의 오프셋(두 포인터 차)이 왜 그 값이 되는지 RetDerived 레이아웃으로 설명하라.
//   (내부 동작 설명은 answer.md 에만. 여기서는 예측만.)
//
// 컴파일: g++ -std=c++17 -O2 problem.cpp && ./a.out

#include <iostream>
#include <cstdint>

struct RetBase {
    int x = 10;
    virtual ~RetBase() {}
};

struct Pad {
    long long p = 0;
    virtual ~Pad() {}   // Pad 를 다형적으로 만들어 primary base(오프셋 0)로 만든다
    virtual void tick() {}
};

struct RetDerived : Pad, RetBase {   // RetBase 는 오프셋 0이 아님
    int y = 20;
};

struct Factory {
    virtual ~Factory() {}
    virtual RetBase* make() = 0;
};

struct ConcreteFactory : Factory {
    RetDerived storage;
    RetDerived* make() override { return &storage; }   // 공변 반환: RetDerived* -> RetBase*
};

int main() {
    ConcreteFactory cf;
    Factory* f = &cf;

    RetBase*    rb = f->make();     // 정적 반환형 RetBase*  (썽크가 조정)
    RetDerived* rd = cf.make();     // 정적 반환형 RetDerived*

    std::cout << std::boolalpha;
    std::cout << rb->x << "\n";                                                   // L1
    std::cout << (reinterpret_cast<char*>(rb) - reinterpret_cast<char*>(rd)) << "\n"; // L2
    std::cout << (static_cast<RetDerived*>(rb) == rd) << "\n";                    // L3
    std::cout << rd->y << "\n";                                                   // L4
    return 0;
}
내 리뷰 · C++
내 답안 · 자동 저장

작성 후 위 해설 보기에서 모범 해설과 대조하세요.