본문 바로가기
[공부]/C++

[C++] 기본 정리 18 - OOP의 다형성(가상 함수, 추상 클래스)

by 개발꿈냥무 2024. 2. 26.

가상 함수

  • 파생 클래스에서 재정의할 것으로 기대하는 멤버 함수
  • 객체의 동적 타입에 따라 실제 호출할 함수가 결정됨
  • virtual 키워드를 사용하여 선언
  • 기초 클래스에서 virtual 키워드로 가상 함수를 선언하면, 파생 클래스에서 재정의된 멤버 함수도 자동으로 가상 함수가 됨 (파생 클래스에서도 virtual 클래스를 명시해주면 좋음)
문법)
virtual 멤버함수원형;

 

 

 

동적 바인딩 (dynamic binding)

바인딩 : 함수 호출 코드에서 어느 블록에 있는 함수를 실행하라는 의미로 해석하는 것

  1. 정적 바인딩 (초기 바인딩) : 함수 호출 코드가 컴파일 타임에 고정된 메모리 주소로 변환되는 것 (C++에서 가상 함수가 아닌 모든 멤버 함수가 해당됨)
  2. 동적 바인딩 (지연 바인딩) : 가상 함수는 프로그램이 '실행'될 때 객체를 결정하므로, 런 타임에 올바른 함수가 실행되도록 함
    • 단, 가상 함수도 결합하는 타입이 분명할 때에는 정적 바인딩을 함
    • 기초 클래스 타입의 포인터나 참조를 통하여 가상 함수가 호출될 때만 동적 바인딩!
예시)
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(); --> 냥!

 

 

추상 클래스의 용도 제한 (다음과 같은 용도로는 사용 불가)

  1. 변수 or 멤버 변수
  2. 함수의 전달되는 인수 타입
  3. 함수의 반환 타입
  4. 명시적 타입 변환의 타입

 

 


참고)

코딩의 시작, TCP School

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com