기본 코드
class Base {
public:
Base() { std::cout << "Base()" << std::endl; }
Base(int a){ std::cout << "Base(int)" << std::endl; }
~Base() { std::cout << "~Base()" << std::endl; }
};
class Derived : public Base {
public:
Derived() { std::cout << "Derived()" << std::endl; }
Derived(int a) { std::cout << "Derived(int)" << std::endl; }
~Derived() { std::cout << "~Derived()" << std::endl; }
};
int main()
{
Derived d2(5);
}
출력은...
Derived(int a) 생성자를 호출하기 전, 상속받은 Base의 기본 생성자를 먼저 호출한다.
Derived(int a) 라고 되어있지만, 컴파일러는 자연스래 Derived(int a) : Base() {} 로 변환하여 진행.
소멸될 때엔 derived 의 소멸자를 부르고, base의 소멸자를 부른다.
cpp를 사용하다보면 다형성을 사용하기 위해 자연스래 기반 클래스의 포인터로 변수를 정의해 두고,
거기에 상속받은 클래스를 대입하여 사용을 하곤 하는데
->>
class Base
{
public:
Base() { std::cout << "Base()" << std::endl; }
Base(int a) { std::cout << "Base(int)" << std::endl; }
~Base() { std::cout << "~Base()" << std::endl; }
virtual void do_something() = 0;
};
class Derived : public Base
{
public:
Derived() { std::cout << "Derived()" << std::endl; }
Derived(int a) { std::cout << "Derived(int)" << std::endl; }
~Derived() { std::cout << "~Derived()" << std::endl; }
void do_something() override { std::cout << "hihi!" << std::endl; };
};
int main()
{
std::unique_ptr<Base> v;
v = std::make_unique<Derived>(5);
v->do_something();
}
위와 같이.
실행을 시켜 보면, 원하는 결과인 "hihi" 는 잘 출력이 되지만, 어째서인지 소멸자가 제대로 불리지 않는다.
v는 Base type이므로, 소멸자가 불릴 때 자연스럽게 Base 소멸자만 불린다.
이걸 해결하기 위해서 소멸자에게 virtual을 붙여주면
class Base
{
public:
Base() { std::cout << "Base()" << std::endl; }
Base(int a) { std::cout << "Base(int)" << std::endl; }
virtual ~Base() { std::cout << "~Base()" << std::endl; }
virtual void do_something() = 0;
};
class Derived : public Base
{
public:
Derived() { std::cout << "Derived()" << std::endl; }
Derived(int a) { std::cout << "Derived(int)" << std::endl; }
~Derived() { std::cout << "~Derived()" << std::endl; }
void do_something() override { std::cout << "hihi!" << std::endl; };
};
int main()
{
std::unique_ptr<Base> v;
v = std::make_unique<Derived>(5);
v->do_something();
}
원하는 결과를 얻을 수 있다.
아니면. virtual을 붙지지 않고 기반 클래스의 소멸자를 protected로 숨겨야 한다.
이번 예제는 unique_ptr 사용하지 말고, 좀 직관적으로 보게 코드를 살짝 바꿔보면
class Base
{
protected:
~Base() { std::cout << "~Base()" << std::endl; }
public:
Base() { std::cout << "Base()" << std::endl; }
Base(int a) { std::cout << "Base(int)" << std::endl; }
virtual void do_something() = 0;
};
class Derived : public Base
{
public:
Derived() { std::cout << "Derived()" << std::endl; }
Derived(int a) { std::cout << "Derived(int)" << std::endl; }
~Derived() { std::cout << "~Derived()" << std::endl; }
void do_something() override { std::cout << "hihi!" << std::endl; };
};
int main()
{
Base* v = new Derived(2);
delete v;
}
이런 식으로.
이렇게 하면, protected로 밖에서 안 보이게 해뒀으니 delete v 부분에서 소멸자에 접근할 수 없다고 에러가 뜬다.
어떻게 해야 하나?
걍 delete v; 를 지워버리면 에러도 없고 마음도 편안하겠지만 소멸자는 절대 불리지 않을 것이다.
그럼 부르려면 어떻게 해야 하나..?
좀만 더 생각해보면, v는 type만 Base*일 뿐, 실제론 Derived class인걸 확인 할 수 있다.
다음과 같이 변경하고
class Base
{
protected:
~Base() { std::cout << "~Base()" << std::endl; }
public:
Base() { std::cout << "Base()" << std::endl; }
Base(int a) { std::cout << "Base(int)" << std::endl; }
virtual void do_something() = 0;
};
class Derived : public Base
{
public:
Derived() { std::cout << "Derived()" << std::endl; }
Derived(int a) { std::cout << "Derived(int)" << std::endl; }
~Derived() { std::cout << "~Derived()" << std::endl; }
void do_something() override { std::cout << "hihi!" << std::endl; };
};
int main()
{
Base* v = new Derived(2);
delete static_cast<Derived*>(v);
}
실행하면, 원하는 결과가 뜬다.
기반 클래스에서 소멸자를 만들 땐, virtual로 만들거나 non-virtual protected로 만들어
1. 자연스래 상속받은 클래스에서 소멸자를 전부 부를 수 있게 하거나,
2. user에게 상속받은 클래스의 소멸자까지 부를 수 있도록 강제해주자.
+)
unique ptr일 시, 소멸자를 따로 지정해주자
class Base
{
protected:
~Base() { std::cout << "~Base()" << std::endl; }
public:
Base() { std::cout << "Base()" << std::endl; }
Base(int a) { std::cout << "Base(int)" << std::endl; }
virtual void do_something() = 0;
};
class Derived : public Base
{
public:
Derived() { std::cout << "Derived()" << std::endl; }
Derived(int a) { std::cout << "Derived(int)" << std::endl; }
~Derived() { std::cout << "~Derived()" << std::endl; }
void do_something() override { std::cout << "hihi!" << std::endl; };
};
void my_Deleter(Base *p) {
delete static_cast<Derived*>(p);
}
int main()
{
std::unique_ptr<Base, decltype(&my_Deleter)> v(new Derived(5), &my_Deleter);
v->do_something();
}
참고 : CppCoreGuideline. C.35
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-dtor-virtual
'C++ > 기타' 카테고리의 다른 글
[C++] DLL로 class export / import - 1 (0) | 2023.03.16 |
---|---|
[C++ Metaprogramming] enable_if / enable_if_t (0) | 2023.03.07 |
[C++ String] string 나누기. string split (0) | 2023.03.04 |
[C++ keyword] noexcept (0) | 2023.03.02 |
[C++ keyword] explicit 란? (4) | 2023.02.25 |