일반적인 Animal과 Dog의 상속관계에서,
// 1-1. 생성자 호출순서
Animal* A = new Animal;
Dog* D = new Dog;

#생성자
부모(Animal)는 생성시, 본인의 생성자가 당연히 호출되고,
자식(Dog)은 부모의 생성자를 호출하고, 본인의 생성자를 호출한다.
// 1-5. 소멸자 호출순서
delete A;
delete D;
#소멸자
부모(Animal)는 소멸시, 본인의 소멸자가 당연히 호출되고,
자식(Dog)은 본인의 소멸자를 호출하고, 부모의 소멸자를 호출한다.

Animal* AD = new Dog; // Dog : public Animal
AD->Eat();
이 코드 때문에 사용하는 것으로 보인다.
Animal을 상속받는 다양한 동물 Cat, Bear 등이 있을거고,
각각을 사용하기 위해, Cat\*, Bear\* 이렇게 선언하는 것보다,
어짜피 상속하면 메서드의 이름도 같은거
그냥 ★ 공통부모인 Animal*을 통해 동일한 이름으로 더 편하게 접근하겠다는 의도로 사용할텐데
Dog객체로 생성한 Animal* AD는,
일반적으로 Eat을 할 때, Dog의 Eat()으로 동작할 것을 기대한다.
하지만, 일반 오버라이딩을 진행한다면, Animal의 Eat()이 실행된다.
#include <iostream>
using namespace std;
class Animal
{
public:
Animal()
{
cout << "Animal : Init\n";
}
~Animal()
{
cout << "Animal : Delete\n";
}
void Eat()
{
cout << "먹었다\n";
}
};
class Dog : public Animal
{
public:
Dog()
{
cout << "Dog : Init\n";
}
~Dog()
{
cout << "Dog : Delete\n";
}
void Eat()
{
cout << "개가 먹었다\n";
}
};
int nCount = 1;
inline void line() { cout << "\n==========================" << nCount << "\n"; nCount++; }
void main()
{
// 1. 정적바인딩 - 함수에 virtual을 붙이지 않은경우
line();
// 1-1. 생성자 호출순서
Animal* A = new Animal;
Dog* D = new Dog;
line();
// 1-2. 업캐스팅
A->Eat(); // 먹었다
D->Eat(); // 개가 먹었다
((Animal*)D)->Eat(); // 먹었다
line();
// 1-3. 핵심
Animal* AD = new Dog;
AD->Eat(); // 먹었다
line();
// 1-4. 다운캐스팅
((Dog*)AD)->Eat(); // 개가 먹었다
line();
// 1-5. 소멸자 호출순서
delete A;
delete D;
cout << "\n";
//delete AD;
delete (Dog*)AD;
정적바인딩은 변수를 생성하면 자료형을 바꾸지 않는다.
★ = 명시한 자료형에 맞게 정직하게 동작
1-2. ((Animal*)D)->Eat(); 에서도
D의 실제 타입은 Dog지만, Animal로 명시적 업캐스팅을 했기에,
Animal의 Eat으로 동작.
1-3. AD의 실제 타입은 Dog지만, Animal로 명시되어있으므로,
같은 이유로 Animal의 Eat으로 동작.
(생성자의 실행과정을 보면, Animal->Dog으로 실행)
1-4. 업캐스팅을 다시 다운캐스팅하면
(실제 개체가 Dog이었기 때문에 가능)
Dog으로 명시되어있으므로, Dog의 Eat으로 동작
1-5. delete AD를 하면 AD가 Animal이므로 Animal의 소멸자 호출.
그렇다면 delete (Dog*)AD는 Dog로 명시했으니,
자식인 Dog, 부모인 Animal순으로 소멸자가 호출될 것이다.

그렇게 된다면 문제가 보인다.
Animal* AD = new Dog로 선언을 했고,
delete AD로 해제를 하는게 뭔가 당연해 보인다.
하지만 그렇게 하면, AD는 실제로 Dog로 생성됐음에도,
Animal의 delete만 진행되어 문제가 발생.그래서 애초에 정적바인딩은
Animal* AD = new Dog; 이런 구문을 사용하지 않는다
Dog* D = new Dog; delete D; 이런식으로 직접 명시할 것이다.
그런데 그거 하기 싫으니까 virtual 붙여서 overriding한다.
class Animal
{
public:
Animal()
{
cout << "Animal : Init\n";
}
virtual ~Animal()
{
cout << "Animal : Delete\n";
}
virtual void Eat()
{
cout << "먹었다\n";
}
virtual void Fly() // 추가
{
cout << "날았다\n";
}
};
class Dog : public Animal
{
public:
Dog()
{
cout << "Dog : Init\n";
}
~Dog()
{
cout << "Dog : Delete\n";
}
void Eat()
{
cout << "개가 먹었다\n";
}
/*
void Fly() // 구현하지 않는다.
{
cout << "개가 날 수 있을까? \n";
}
*/
};
{
// 2. 동적바인딩 - 함수에 virtual을 붙인 경우 - 가상함수테이블
line();
// 1-1. 생성자 호출순서
Animal* A = new Animal;
Dog* D = new Dog;
line();
// 1-2. 업캐스팅
A->Eat(); // 먹었다
D->Eat(); // 개가 먹었다
((Animal*)D)->Eat(); // 개가 먹었다 << 부모의 "먹었다" 가 동작하지 않는다
D->Animal::Eat(); // 먹었다 << 이렇게 직접 명시하면 부모의 "먹었다" 동작
line();
// 1-3. 핵심
Animal* AD = new Dog;
AD->Eat(); // 개가 먹었다 << 동적바인딩되어 Dog의 Eat()
AD->Fly(); // 자식에서 구현되지 않은경우, 부모의 것이 호출됨
line();
// 1-4. 다운캐스팅
((Dog*)AD)->Eat(); // 개가 먹었다 << 되는이유?
line();
// 1-5. 소멸자 호출순서
delete A;
delete D;
cout << "\n";
//delete (Dog*)AD;
delete AD; // 이것도 virtual이기 때문에, Dog의 소멸자 호출
}

1-2. ((Animal*)D)->Eat()를 했는데도, 부모의 Eat이 동작하지 않았다.
이건 왜그렇지??
Animal* D = new Dog 와 같은 원리인 것 같다.
1-3. 기본적으로 이 행위를 위해 동적바인딩을 사용한다고 생각한다.
Animal로 하위 클래스들을 동일하게 관리할 수 있게 된다.
virtual이 자식에서 구현되지 않은 경우는,
부모의 virtual 함수가 그대로 실행된다.만약 부모의 virtual 함수에도 구현이 되어있지 않다면,
오류발생

Animal* AD = new Dog
생성자는 애초에 객체를 new하여 생성하면,
부모 - 자식 순으로 알아서 생성을 해준다.
소멸자는 그렇지 않았다.
delete AD라는 것은, delete (Animal*) AD라는 것이고,
소멸자는 대입하는 과정이 없기 때문에, 실제 객체의 타입을 인식할 수 없으므로,
Animal의 소멸자만 호출.
따라서 부모클래스에서 가상함수를 사용한다는 것은,
부모클래스의 소멸자도 반드시 virtual이어야 한다는 말.
가상으로 소멸자를 해두지 않는다면,
소멸자를 정적바인딩처럼 볼 수 있고,
Animal, 즉 부모의 것으로만 인식하게 될 것이다.
virtual이라고 다른 소멸자가 있을 것으로 판단할 여지를 주지 않았기 때문.
(C++는 기본 정적바인딩, JAVA는 기본 동적바인딩)
그러면 가상생성자라는걸 사용한다면 어떻게 될까
생각해본다.