// [Animal.h]
class Animal
{
public :
Animal(int age);
virtual ~Animal();
virtual void Move() const;
virtual void Speak() const;
int GetAge() const;
private:
int mAge;
};
// [Cat.h]
class Cat : public Animal
{
public:
Cat(int age);
virtual void Move() const;
virtual void Speak() const;
};
// [Cat.cpp]
Cat::Cat(int age)
// OOP의 중요한 개념중 하나는 부모 클래스에서 정상적으로 동작 할 것이라고 기대하며
// 데이터만 넘겨주면 모든게 준비되어야 한다.
: Animal(age)
{
}
public Cat(int age, string name)
{
super(age);// 부모 클래스의 이름을 알 필요가 없어서 간결...
}
[Animal.h]
Animal::Animal()
:mAge(0)
{}
// [Cat.h]
// 암시적으로 부모의 기본 생성자(Animal()) 호출
Cat::Cat()
{}
[Animal.h]
Animal::Animal(int age)
: mAge(age)
{}
[Cat.h]
// 암시적으로 부모의 기본 생성자(Animal()) 호출
// 그러나 컴파일 에러... 부모의 기본 생성자 없어졌기 때문에
Cat::Cat(int age, const string& name)
{}
[Animal.h]
Animal::~Animal()
{}
//[Cat.h]
// 암시적으로 부모의 기본 생성자(Animal()) 호출
Cat::~Cat()
{
delete mName;
// 여기서 암시적으로 ~Animal() 호출
}
Cat* myCat = new Cat(5, "coco");
Cat* yourCat = new Cat(5, "Mocha");
myCat->GetName();
yourCat->GetName();
myCat->GetName();
yourCat->GetName();
/*
둘의 동작은 완전히 일치 하는데 메모리를 따로 잡을 필요가 있을까??
위 두 함수의 유일한 차이점은 호출해 주는 개체의 메모리이다.
그래서 개체마다 멤버 함수들은 메모리에 따로 잡혀 있는것이 아니라 메모리 한 곳에 있다.
(마치 중복 코드는 나쁘기에 피하라는 원리와 비슷...)
*/
멤버함수는 '컴파일' 시 '딱 한번만 메모리에 할당됨'.
멤버 함수를 호출 할 때 일어나는 일.
// GetName 멤버 함수는 인자가 전혀 없지만
// 실제 컴파일 단에서 이렇게 변환된다.
// 어셈블리로 보면 멤버 함수 호출시 ecx 레지스터에 개체의 메모리 넘김
char* Cat::GetName(Cat* this)
{
return this->Name;
}
"멤버 함수는 개체의 소유가 아니라 클래스의 소유라고 생각하면 된다."
정리하자면, 개체의 멤버 함수를 호출할 때는 같은 메모리에 위치한 멤버 함수를 호출하고 차이점은
첫번째 인자로 넘겨지는 개체의 주소값이다
정적 바인딩
class Animal
{
public:
void Speak();
};
class Cat : public Animal
{
public:
void Speak();
};
// "Cat : 정적 타입"
// "Cat : 동적 타입"
Cat* myCat = new Cat();
myCat->Speak();// Cat 의 Speak 함수 호출
// "Animal : 정적 타입"
// "Cat : 동적 타입"
Animal* myAnimal = new Cat();
myAnimal->Speak();// Aniaml의 Speak 함수 호출
class Animal
{
public:
virtual void Speak();
};
class Cat : public Animal
{
public:
void Speak();
};
// Cat의 Speak()함수 호출
// 동적 타입에 바인딩 되기에..
Animal* myAnimal = new Cat();
// 가상 함수는 언제나 자식 클래스의 멤버 함수가 호출 됨
// 동적 바인딩을 늦은 바인딩이라고도 함,
// 이유는 실행중에 호출될 함수가 결정 되니까...
Q : Speak()함수 어디 있니 ???
A : 어디보자... 가상 테이블이 0xAAAAAAAA에 있고 두번째 함수니까
0xAAAAAAAE 에 존재하겠네...
class Animal
{
public:
~Animal()
{
}
}
class Cat : public Aniaml
{
public:
~Cat()
{
delete Name;
}
}
// Cat 클래스의 소멸자도 정적바인딩 되어 있다. 그렇다는 뜻은...
Animal* animal = new Cat("coco");
// ~Cat() 소멸자가 아닌 ~Animal()의 소멸자가 호출되어 Memory Leak이 발생
delete animal;
해결책은 virtual ~Animal()로 가상 소멸자를 만들어 주면 됨
다중 상속
class Animal
{
public void Speak() = 0; // 순수 가상 함수
};
순수 가상 함수
인터페이스
class IFlyAble
{
public:
virtual void Fly() = 0;
}