C++/C++ 기본

가상 함수와 추상 클래스

검정비니 2019. 1. 21. 00:52
728x90
반응형

가상 함수와 추상 클래스


1. 상속 관계에서의 함수 중복


파생 클래스에 기본 클래스와 동일한 형식의 함수가 중복 작성된 경우, 기본 클래스에 대한 포인터로는 기본 클래스에 선언된 함수를 호출하고, 파생 클래스에 대한 포인터로는 파생 클래스에 선언된 함수를 호출한다.





2. 가상 함수와 오버라이딩


- 가상 함수(virtual function)란 virtual 키워드로 선언된 멤버 함수


컴파일러에게 자신에 대한 호출을 실행 시간까지 미루도록 지시한다.


- 파생 클래스에서 기본 클래스에 선언된 가상 함수와 완전히 동일한 타입의 함수를 재정의하는 것을 함수 오버라이딩 혹은 함수 재정의라고 한다.


- 파생 클래스에서 가상 함수를 오버라이딩하고 기본 클래스의 포인터로 파생 클래스의 객체를 가리킬 때, 가상 함수를 호출하면 무조건 파생 클래스에서

   오버라이딩한 가상 함수가 호출된다. 이것을 동적 바인딩이라고 한다.


- 가상 함수의 이름, 매개 변수 개수와 타입, 리턴 타입까지 일치할 때 오버라이딩이 성공한다.


- 가상 함수의 virtual 선언은 상속되므로 오버라이딩 시 virtual을 생략할 수 있다.


- 파생 클래스에서 범위 지정 연산자(::)를 사용하면 기본 클래스의 가상 함수를 강제로 호출할 수 있다. 컴파일러에 의해 정적 바인딩된다.


- 가상 소멸자는 virtual로 선언된 소멸자로서 사용을 권장한다.


기본 클래스에 대한 포인터를 이용하여 파생 클래스의 객체를 소멸시켜도, 소멸자가 virtual로 선언되어 있으면, 파생 클래스의 소멸자가 실행되고 뒤이어 기본 클래스의 소멸자가 실행되는 정상적인 과정이 진행된다.


- 생성자는 가상 함수로 만들 수 없으며, 가상 함수를 생성자 내에서 호출해도 동적 바인딩이 일어나지 않는다.





2 - 1. 동적 바인딩


가상 함수를 호출하는 코드를 컴파일할 때, 컴파일러는 바인딩을 실행 시간에 결정하도록 미루어둔다. 나중에 가상 함수가 호출되면, 실행 중에 객체 내에 오버라이딩된 가상 함수를 동적으로 찾아 호출한다. 이 과정을 동적 바인딩(dynamic binding)이라고 부른다. 오버라이딩은 파생 클래스에서 재정의한 가상 함수의 호출을 보장받는 선언이다.


동적 바인딩은 실행 시간 바인딩(run-time binding) 혹은 늦은 바인딩(late binding)이라고도 부른다.


기본 클래스의 객체에 대해서는 가상 함수가 호출된다고 하더라도 동적 바인딩은 일어나지 않는다. 객체 내에 오버라이딩된 가상 함수가 없기 때문이다. 동적 바인딩은 파생 클래스의 객체에 대해, 기본 클래스의 포인터로 가상 함수가 호출될 때 일어난다.


동적 바인딩이 발생하는 구체적인 경우는 다음과 같다:


1) 기본 클래스 내의 멤버 함수가 가상 함수 호출


2) 파생 클래스 내의 멤버 함수가 가상 함수 호출


3) main()과 같은 외부 함수에서 기본 클래스의 포인터로 가상 함수 호출


4) 다른 클래스에서 가상 함수 호출


가상 함수를 호출하면, 무조건 동적 바인딩을 통해 파생 클래스에 오버라이딩된 가상 함수가 실행된다.





2 - 2. 범위 지정 연산자


컴퓨터 언어 이론에서 범위 규칙(scope rule)이란 동일한 이름(identifier)의 변수나 함수가 여러 곳에 선언되어 있을 때, 가장 가까운 범위에 선언된 이름을 사용하는 규칙이다. 클래스나 블록 내에 선언된 이름과 동일한 이름이 전역 범위(global area)에 선언되면, 전역 범위에 선언된 이름은 클래스나 블록으로부터 숨겨지게(hidden) 된다. 이때 다음과 같이 범위 지정 연산자(::)를 사용하면 숨겨진 전역 범위의 이름(함수나 변수 등)에 접근할 수 있다.


#include <iostream>
using namespace std;

int n=11; // 전역 변수

int main() {
int n = 3; // 지역 변수
cout << ::n << endl; // 전역 변수 n(11) 출력
cout << n << endl; // 지역 변수 n(3) 출력
}



#include <iostream>
using namespace std;

void sendMessage(char *msg) { cout << msg << endl; }

class Window {
public:
void sendMessage(char * msg) { cout << "window msg : " << msg << endl; }
void run() {
::sendMessage("Global Hello");
Window::sendMessage("Local Hello");
}
};






3. 추상 클래스


-함수의 코드가 없고 선언만 있는 가상 함수를 순수 가상 함수(pure virtual function)라고 부른다.


- 최소한 하나의 순수 가상 함수를 가진 클래스를 추상 클래스(abstract class)라고 부른다.


- 추상 클래스는 온전한 클래스가 아니므로 인스턴스를 생성할 수 없다.


- 추상 클래스에 대한 포인터는 선언할 수 있다.


- 추상 클래스는 상속을 위한 기본 클래스로서 파생 클래스에서 구현할 함수의 원형을 알려주는 인터페이스의 역할을 한다.


- 추상 클래스 구현이란 파생 클래스에서 추상 클래스를 상속받아 순수 가상 함수를 모두 구현하는 것이다.


- 추상 클래스를 상속받은 파생 클래스가 순수 가상 함수를 모두 구현하지 않는 경우 역시 추상 클래스가 된다.


- 추상 클래스로 기본 방향을 잡아놓고 파생 클래스에서 그 목적에 따라 서로 다르게 구현할 수 있으므로, 프로그램 설계와 구현을 분리할 수 있다.



class Shape {
public:
virtual void draw() = 0; // 순수 가상 함수는 함수 구현 없이 뒤에 "=0"을 붙인다.
}


반응형

'C++ > C++ 기본' 카테고리의 다른 글

템플릿과 표준 템플릿 라이브러리  (0) 2019.06.30
c++ 상속  (0) 2019.01.09
연산자 함수는 멤버 함수나 프렌드 함수 중 어떤 것이 바람직한가?  (0) 2019.01.01
C++ 프렌드 개념  (0) 2018.08.02
static 멤버  (0) 2018.07.31