가상 함수
- 파생 클래스에서 재정의할 것으로 기대하는 멤버 함수
- 객체의 동적 타입에 따라 실제 호출할 함수가 결정됨
- virtual 키워드를 사용하여 선언
- 기초 클래스에서 virtual 키워드로 가상 함수를 선언하면, 파생 클래스에서 재정의된 멤버 함수도 자동으로 가상 함수가 됨 (파생 클래스에서도 virtual 클래스를 명시해주면 좋음)
문법)
virtual 멤버함수원형;
동적 바인딩 (dynamic binding)
바인딩 : 함수 호출 코드에서 어느 블록에 있는 함수를 실행하라는 의미로 해석하는 것
- 정적 바인딩 (초기 바인딩) : 함수 호출 코드가 컴파일 타임에 고정된 메모리 주소로 변환되는 것 (C++에서 가상 함수가 아닌 모든 멤버 함수가 해당됨)
- 동적 바인딩 (지연 바인딩) : 가상 함수는 프로그램이 '실행'될 때 객체를 결정하므로, 런 타임에 올바른 함수가 실행되도록 함
- 단, 가상 함수도 결합하는 타입이 분명할 때에는 정적 바인딩을 함
- 기초 클래스 타입의 포인터나 참조를 통하여 가상 함수가 호출될 때만 동적 바인딩!
예시)
class A { public: virtual void Print(); };
class B : public A { public: virtual void Print(); };
//메인함수
A* ptr;
A obj_a;
B obj_b;
ptr = &obj_a; ptr->Print(); --> A 클래스의 함수가 호출됨
ptr = &obj_b; ptr->Print(); --> B 클래스의 함수가 호출됨
가상 함수 테이블 (vtbl, virtual function table)
C++는 가상 함수의 정의와 동작 방식만을 규정하고, 그에 따른 구현은 컴파일러마다 다름
--> 컴파일러가 가상 함수를 다루는 (일반적인) 방식 : 가상 함수 테이블
C++ 컴파일러는 각각의 객체마다 가상 함수 테이블을 가리키는 포인터를 저장하는 숨겨진 멤버를 하나씩 추가함
+ 가상 함수를 단 하나라도 갖는 클래스에 대해서 가상 함수 테이블을 작성
--> 가상 함수 테이블 : 해당 클래스의 객체들을 위해 선언된 가상 함수들의 주소가 저장됨
--> 가상 함수를 호출하면, C++ 프로그램은 vtbl에 접근해 필요한 함수의 주소를 찾아서 호출
가상 함수 사용하면 함수의 호출 과정이 복잡해지므로
메모리와 실행 속도 측면에서 약간의 부담이 있음
가상 소멸자
C++에서 기초 클래스의 소멸자는 반드시 가상으로 선언해야 함!!
Person* hong = new Student;
...
delete hong;
--> ~Student() 소멸자가 아닌, ~Person() 소멸자가 호출되어 메모리가 정상적으로 해체되지 않을 수 있음
--> Person 클래스의 소멸자를 가상으로 선언한다면, 위의 구문은 정상적으로 ~Student() 소멸자를 호출할 것임
- 가상 함수 : 반드시 재정의해야만 하는 게 아닌, 재정의가 가능한 멤버 함수
- 순수 가상 함수 : 파생 클래스에서 '반드시 재정의'해야하는 멤버 함수 (재정의하지 않으면 사용 불가)
순수 가상 함수 문법 : 가상 함수 선언부 끝에 "=0"을 추가
virtual 멤버함수원형 = 0;
추상 클래스 (abstract class)
- 하나 이상의 순수 가상 함수를 포함하는 클래스
- OOP의 중요 특징인 다형성을 가진 함수 집합을 정의할 수 있게 함 (반드시 사용되어야 하는 멤버 함수를 추상 클래스에 순수 가상 함수로 선언하면, 파생된 모든 클래스에서 이 가상 함수를 반드시 재정의해야 함!!)
- 동작이 정의되지 않은 순수 가상 함수를 포함하기 때문에, 인스턴스를 생성할 수 없음 (추상 클래스는 먼저 상속을 통해 파생 클래스를 만들고, 그 클래스에서 순수 가상 함수를 모두 오버라이딩해야지만 파생 클래스의 인스턴스 생성이 가능함)
- 추상 클래스 타입의 포인터와 참조는 바로 사용 가능
class Animal {
public:
virtual ~Animal() {} // 가상 소멸자 선언
virtual void Cry()=0; // 순수 가상 함수 선언 (파생 클래스는 이 함수를 오버라이딩해야만 인스턴스 생성 가능)
};
class Dog : public Animal {
public:
virtual void Cry() { cout << "멍!\n"; }
};
class Cat : public Animal {
public:
virtual void Cry() { cout << "냥!\n"; }
};
// 메인함수
Dog dog;
dog.Cry(); --> 멍!
Cat cat;
cat.Cry(); --> 냥!
추상 클래스의 용도 제한 (다음과 같은 용도로는 사용 불가)
- 변수 or 멤버 변수
- 함수의 전달되는 인수 타입
- 함수의 반환 타입
- 명시적 타입 변환의 타입
참고)
코딩교육 티씨피스쿨
4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등
tcpschool.com
'[공부] > C++' 카테고리의 다른 글
[C++] 기본 정리 17 - OOP의 상속성(파생 클래스, 멤버 함수 오버라이딩, 다중 상속) (0) | 2024.02.26 |
---|---|
[C++] 기본 정리 16 - OOP의 캡슐화(프렌드, 정적 멤버, 상수 멤버) (0) | 2024.02.25 |
[C++] 기본 정리 15 - 연산자 오버로딩 (1) | 2024.02.24 |
[C++] 기본 정리 14 - 생성자, 복사 생성자, 소멸자 (0) | 2024.02.24 |
[C++] 기본 정리 13 - 클래스, 객체 지향 프로그래밍, this 포인터 (1) | 2024.02.24 |