동적 정적 바인딩
사전 준비
* 먼저 다형성에 대한 개념을 알고 있어야 한다.
https://licktwice.tistory.com/59https://licktwice.tistory.com/59
[JAVA]자바 - 다형성 Polymorphism
다형성 Polymorphism OOP의 4대 특성 * 다형성은 OOP(Object Oriented Programming)의 4대 특성 중 하나로 주류 OO언어에 꼭 들어가는 개념이다. * 그중에서도 가장 핵심이라고 생각되는 개념이다. 넓은 의미의..
licktwice.tistory.com
* 즉, 다형성을 사용하기 위해서는 동적 바인딩이 필요하다.
사전 준비
* 동적 바인딩과 정적 바인딩의 차이를 알기 위해서는 상속관계와 함수 오버라이딩이 필요하다.
* 다형성을 안다면 자식 객체를 부모 포인터형으로 접근할 수 있다는 것을 안다.
* 부모 포인터형으로 자식 객체를 접근하는 것은
자식 객체에서 부모 부분만을 접근할 수 있다.
* 즉, 자식 객체라도 부모 포인트형으로 접근하면
부모 클래스에서 정의된 변수와 함수만을 접근할 수 있다.
* 부모 포인터형으로 자식 객체를 접근 가능하다?
부모 포인터형은 가리키는 대상이 부모 클래스라고 생각한다.
부모 클래스에 있는 부분만을 접근한다.
* 어떻게 부모 클래스에 있는 부분만을 특정해서 접근할 수 있을까?
자식 객체 생성은 부모 생성자를 먼저 호출하기 때문이다.
부모 생성자가 먼저 부모 부분 메모리를 생성하고,
이후에 자식 생성자가 바로 뒤에 자식 메모리를 이어 만든다.
사전 준비 관련 예시 코드
class Animal {
public:
int mAge;
void Speak() { cout << "동물입니다." << endl; }
Animal(int age) : mAge(age) {}
};
class Dog :public Animal {
public:
string mName;
void Speak() { cout << "멍멍!" << endl; }
Dog(int age, string name) : Animal(age), mName(name) {}
};
int main() {
Animal* animal = new Dog(10, "까망이");
return 0;
}
* 자식 객체라도 부모 포인터형으로 접근하면 부모 부분만 접근 가능하다.
기본은 정적 바인딩
* 위처럼 포인터형으로 내부를 판단하는 방법을 정적 바인딩이라고 한다.
* 정적 바인딩은 함수 오버라이딩에서도 사용된다.
조건 1. 부모 클래스에 함수가 있다.
조건 2. 자식 클래스가 함수를 오버라이딩했다.
int main() {
Dog* dog = new Dog(10, "까망이");
Animal* animal = dog;
cout << "자식 포인터: ";
dog->Speak();
cout << "부모 포인터: ";
animal->Speak();
return 0;
}
* animal*는 부모 함수가 호출된다.
dog*는 자식 함수가 호출된다.
* 포인터형은 컴파일 시에도 알 수 있기 때문에
정적 바인딩은 컴파일 타임에 호출될 함수가 결정된다.
* 정적 바인딩은 C++에서 기본이다.
virtual 키워드를 사용해야 동적 바인딩을 사용할 수 있다.
* 자바는 기본이 동적 바인딩이다.
final 키워드를 사용해야 정적 바인딩을 사용할 수 있다.
동적 바인딩
* 동적 바인딩을 어떤 포인터형을 사용하든 상관없이
실제 객체를 기준으로 함수를 호출한다.
* 실제 객체를 알아야 하기 때문에 실행 중에 호출 함수를 판단한다.
즉, 동적 바인딩을 실행 중에 호출될 함수가 결정된다.
* 동적 바인딩이 있어야 다형성의 장점인
같은 부모를 가지고 있는 자식 클래스들이
동일한 함수 시그니쳐를 가지고
각각의 다른 동작을 구현한다.
virtual 키워드
* 동적 바인딩을 사용하기 위해서는 virtual 키워드를 사용해야 한다.
class Animal {
public:
virtual void Speak() { cout << "동물입니다." << endl; }
};
class Dog :public Animal {
public:
void Speak() { cout << "멍멍!" << endl; }
};
int main() {
Dog* dog = new Dog();
Animal* animal = dog;
cout << "자식 포인터: ";
dog->Speak();
cout << "부모 포인터: ";
animal->Speak();
return 0;
}
* virtual 키워드는 함수에만 사용 가능하다.
함수 앞에 virtual를 붙이면 된다.
* 이런 함수를 가상 함수라고 한다.
* virtual은 상속되기 때문에 자식 오버라이딩 함수는 따로 써주지 않아도 된다.
(가독성을 위해 써줘도 좋다.)
* 언제나 자식 클래스의 멤버 함수가 호출됨을 볼 수 있다.
가상함수 내부 동작
* 클래스에 가상함수가 하나라도 있다면
객체에 내부적으로 포인터가 하나 생성된다. (객체 크기 상승)
클래스 전용 가상 테이블이 생성된다.
(알아서 만들어짐)
* 객체에 생성된 포인터는 가상 테이블을 가리킨다.
가상 테이블은 클래스에 가상 함수를 가리킨다.
* Animal 객체는 Animal 가상테이블 주소를 가지고 있다.
Dog 객체는 Dog 가상테이블 주소를 가지고 있다.
* 가상 테이블은 클래스의 모든 가상 함수 주소를 가지고 있다.
* 즉, 동적 바인딩은 객체가 가지고 있는
포인터를 사용하기 때문에 실행 중에 판단된다.
* 객체 >> 가상 테이블 >> 함수라는
2중 포인터를 사용하는 방식인 것이다.
귀여운 그림은 쭐어님이 그리셨습니다.
'C++' 카테고리의 다른 글
[C++] 참조 Reference (8) | 2022.07.04 |
---|---|
[C++] 가상 소멸자 (0) | 2022.07.02 |
[C++] friend 키워드 (2) | 2022.06.23 |
[C++] 객체 배열 생성 및 소멸 (Java와 다른점) (0) | 2022.06.22 |
[C++] C++에서 C 라이브러리 표기법 (0) | 2022.06.22 |