일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- photon fusion2
- gas
- local prediction
- nanite
- animation
- C++
- 언리얼엔진
- Aegis
- attribute
- MAC
- 게임 개발
- 보안
- 유니티
- os
- rpc
- stride
- widget
- Multiplay
- 언리얼 엔진
- ability task
- unity
- gameplay tag
- UI
- listen server
- gameplay effect
- Replication
- gameplay ability system
- 게임개발
- Unreal Engine
- CTF
- Today
- Total
Replicated
[C++] 상속 시 생성자 탐구 본문
기본 원칙
1. 기본 클래스의 생성자가 먼저 호출됨
2. 그 후 멤버 객체들의 생성자가 호출
3. 마지막으로 파생 클래스의 생성자 본문이 실행
#include <iostream>
using namespace std;
class Base {
public:
Base() { cout << "Base constructor\n"; }
};
class Derived : public Base {
public:
Derived() { cout << "Derived constructor\n"; }
};
출력:
Base constructor
Derived constructor
생성자 초기화 리스트와 기본 클래스
class Base {
public:
Base(int x) { cout << "Base(int)\n"; }
};
class Derived : public Base {
public:
Derived(int x) : Base(x) {
cout << "Derived(int)\n";
}
};
파생 클래스 생성자는 기본 클래스 생성자를 초기화 리스트에서 명시적으로 호출 가능
* 기본 클래스에 기본 생성자 없으면 파생 클래스는 명시적으로 생성자 호출 필수
생성자의 상속
class Base {
public:
Base(int x) { cout << "Base(int)\n"; }
};
class Derived : public Base {
public:
using Base::Base; // Base 생성자 상속
};
Derived d(10); → Base(int) 호출됨.
* 기본 클래스의 복사, 이동 생성자는 자동으로 상속되지 않음
* Derived 자체 멤버는 여전히 초기화 필요
다중 상속에서의 생성자 호출
class A { public: A() { cout << "A\n"; } };
class B { public: B() { cout << "B\n"; } };
class C : public A, public B {
public:
C() { cout << "C\n"; }
};
출력:
A
B
C
선언 순서대로 호출됨
가상 상속과 생성자
class A { public: A() { cout << "A\n"; } };
class B : virtual public A { public: B() { cout << "B\n"; } };
class C : virtual public A { public: C() { cout << "C\n"; } };
class D : public B, public C {
public:
D() : A() { cout << "D\n"; } // A를 여기서 호출해야 함
};
- A는 버츄얼로 상속되었으므로 한 번만 호출됨
출력:
A
B
C
D
* 가상 상속
- 다이아몬드 상속 문제를 해결하기 위해 도입한 개념 (여러 경로를 통해 동일한 기본 클래스가 중복 상속되는 것)
class A {
public:
void hello() { std::cout << "Hello from A\n"; }
};
class B : public A {};
class C : public A {};
class D : public B, public C {};
이 경우 D 안에 A가 두 번 존재
D d;
d.hello(); // 컴파일 에러: 어떤 A의 hello인지 모름
D는 두 개의 A 인스턴스를 가짐 -> A::hello() 호출 시 모호성 문제가 생김
class A {
public:
void hello() { std::cout << "Hello from A\n"; }
};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};
그래서 가상 상속 도입
A를 가상으로 상속하고, D는 A의 단일 인스턴스만 가지게 됨
* 이를 위해 vptr 및 오프셋을 이용해 객체 내부 구조를 다르게 구성함
class A {
public:
A(int x) { std::cout << "A(" << x << ")\n"; }
};
class B : virtual public A {
public:
B() : A(0) { std::cout << "B()\n"; }
};
class C : virtual public A {
public:
C() : A(0) { std::cout << "C()\n"; }
};
class D : public B, public C {
public:
D() : A(42) { std::cout << "D()\n"; }
};
단, 가상 기본 클래스의 생성자는 가장 하위 클래스에서 직접 호출해야 함
B, C는 구현해도 무시됨
생성자와 객체 슬라이싱
class Base {
public:
Base() { cout << "Base()\n"; }
};
class Derived : public Base {
public:
Derived() { cout << "Derived()\n"; }
};
Base b = Derived(); // slicing 발생, Derived 부분 잘림
* 객체 슬라이싱
- 파생 클래스 객체를 기본 클래스 타입으로 값 복사할 때 파생 클래스 고유의 멤버가 잘려나가고 기본 클래스 부분만 복사되는 현상
#include <iostream>
using namespace std;
class Base {
public:
int a = 1;
void show() { cout << "Base: a = " << a << endl; }
};
class Derived : public Base {
public:
int b = 2;
void show() {
cout << "Derived: a = " << a << ", b = " << b << endl;
}
};
int main() {
Derived d;
Base b = d; // 객체 slicing 발생
b.show(); // Base::show() 호출
// b.b는 존재하지 않음. Derived의 b 잘림
}
Base는 b를 모름.
실행 결과: a 부분만 나옴
피하려면?
1. 객체를 값으로 다루지 말고 포인터/참조로 다뤄야 함
2. 가상 함수 사용 시 항상 참조나 포인터 기반으로 접근
생성자와 템플릿 상속
template <typename T>
class Base {
public:
Base(T val) { cout << "Base(" << val << ")\n"; }
};
class Derived : public Base<int> {
public:
using Base<int>::Base;
};
템플릿 기반 상속에서는 생성자가 자동으로 상속되지 않음
애초에 C++에서 파생 클래스는 기본 클래스의 생성자를 호출 가능한 구조임
생성자 상속은 using.. 수동 상속임 (C++ 11 이상)
class Base {
public:
Base(int x) { std::cout << "Base(" << x << ")\n"; }
};
class Derived : public Base {
public:
Derived(int x) : Base(x) {} // ← 직접 호출
};
이건 호출임
class Derived : public Base {
public:
using Base::Base; // ✅ Base의 생성자들을 Derived에 "상속"
};
이게 상속
explicit 생성자와 상속
class Base {
public:
explicit Base(int x) { cout << "Base(" << x << ")\n"; }
};
class Derived : public Base {
public:
Derived(int x) : Base(x) {}
};
기본 클래스 생성자가 explicit인 경우 파생 클래스에서 명시적으로 전달해야 함
explicit 생성자?
암시적 변환을 막는 키워드
- 호출 생략해도 기본 생성자는 암시적으로 호출되는데, explicit 선언하면 명시적으로 호출해야 한다는 것
- 인자 필요한 생성자도 자동 호출 안됨
A <- B <- C 상속 구조에서 C가 B나 A의 생성자를 명시적 호출 시 순서는 어떻게 바뀌는가?
=> 생성자 실행 순서는 항상 기본 클래스 -> 중간 클래스 -> 파생 클래스
명시적으로 호출해봐야 실행 순서 안바뀜
소멸자 호출 순서?
class A {
public:
A() { std::cout << "A constructor\n"; }
virtual ~A() { std::cout << "A destructor\n"; }
};
class B : public A {
public:
B() { std::cout << "B constructor\n"; }
~B() { std::cout << "B destructor\n"; }
};
class C : public B {
public:
C() { std::cout << "C constructor\n"; }
~C() { std::cout << "C destructor\n"; }
};
int main() {
C obj;
}
결과..
A constructor
B constructor
C constructor
C destructor
B destructor
A destructor
안전하게 생성, 제거하기 위해 이 순서는 고정임
'지식' 카테고리의 다른 글
[Math] 외적 (0) | 2025.05.28 |
---|---|
[Math] 내적 (0) | 2025.05.28 |
[C++] virtual 탐구 (0) | 2025.05.27 |
[C++] L Value & R Value (0) | 2025.05.27 |
[DS] C++ Deque 랜덤 액세스 O(1)? (0) | 2025.03.21 |