Programming/C/C++

[펌] C++, 가상함수,순수가상함수

blueasa 2010. 3. 14. 00:44

[ 가상함수(Virtual Function) ]

1. 가상함수 개요

■ 가상함수(Virtual Function)란?

- 실행시 사용된 객체에 의해 실행 코드가 결정되는 함수

- 지정자 virtual로 선언됨 지정자 virtual은 선언문에 존재함.

class Animal {

  public :

    virtual void breathe();

};

 

class Fish : public Animal {

   public :

    virtual void breathe();

};

 

■ 가상 함수의 호출

- 기반클래스의 포인터가 파생클래스의 객체를 가리킬 때, 그 포인터를 통해 가상함수를 호출하면 파생 클래스에서 재정의한 함수가 호출됨.

Animal* a = new Fish;

a -> breathe();    // Fish::breathe 호출

→ a->breathe(); 는 Fish::breathe 를 호출함. 즉, a->breathe(); 는 함수명으로는 Animal::breathe를 호출하지만, 내용으로는 Fish::breathe 가 호출되는 것임.

 

■ 기반클래스의 멤버함수가 virtual 을 갖고 있을 때

- 파생클래스에서 재정의한 함수는 virtual 을 생략해도 저절로 가상함수가 됨.

- 그러나 기반클래스의 가상 함수를 파생클래스에서 재정의할  때도 virtual 을 명시해주는 것이 관례임. (소스코드 이해가 수월하기 위해서...)

 

2. 가상함수의 특징

■ 동적바인딩(Dynamic Binding)

- 가상함수는 동적결합(Dynamic Binding)을 함. (코드만 봐서는 어떤 코드가 실행될지 정해지지 않은 상태. 즉, 객체를 생성하는 시점에 생성조건에 따라 실행코드가 결정된다.)

- 일반함수는 정적 결합을 하는 반면, 가상 함수는 동적 결합을 함.

 

■ 가상함수(동적 바인딩)의 장단점

- 장점 : 실행시 다형성(Runtime Polymorphism) 이라는 융통성을 갖게 됨. (상황에 생성하는 조건에 따라 실행 코드를 변경할 수 있다.)

- 단점 : 가상함수를 사용하면 실행속도에서는 손해

  

3. 가상 함수 테이블 (Virtual Function Table)

■ vtable (Virtual Function Table) 이란?

- 가상 함수에 대한 포인터 배열

- 가상 함수를 사용하는 클래스의 각 객체는 vtable을 가리키는 vptr이라는 숨겨진 포인터를 갖고 있음.

- 가상함수를 포함한 클래스의 객체를 생성할 때 vtable이 만들어짐.

- 객체에서 가상함수를 호출하면 vptr를 통해 vtable에 있는 해당함수를 찾아 수행시킴.

 

■ 파생클래스에서의 vtable

- 기반클래스의 객체로부터 vtable을 물려 받아 수정 및 확장함.

- 파생클래스에서 재정의한 가상함수가 있다면 기반클래스의 가상 함수에 대한 포인터가 들어 있던 곳을 파생클래스에서 재정의한 가상함수에 대한 포인터로 변경함.

- 기반클래스의 포인터가 파생클래스의 오브젝트를 가리킬 때 그 포인터를 통해 가상 함수를 호출하면 파생클래스에서 수정한 vtable 을 사용하게 되므로 파생 클래스에서 재정의한 함수가 호출됨.

 

4. 가상 소멸자

■ 상속 관계에서 기반클래스의 포인터가 파생클래스의 동적 객체를 가리킬 때 그 포인터를 통해 동적 객체를 제거하면...?

기본적으로 기반 클래스의 소멸자만 호출되고 파생 클래스의 소멸자는 호출되지 않음.

class Animal {

};

 

class Fish : public Animal {

};

 

Animal* a = new Fish;

delete a;

 

■ 기반클래스의 포인터가 파생클래스의 동적 객체를 가리키고, 그 포인터를 통해 동적 객체를 제거했을 때 파생클래스의 소멸자까지 호출되도록 하려면...?

기반 클래스의 소멸자를 다음과 같이 가상 소멸자(Virtual Destructor)로 만들어야 함.

class Animal {

  public :

    Animal();

    virtual ~Animal();

};

 

class Fish : public Animal {

  public :

    Fish();

    virtual ~Fish();

};

 

Animal* a = new Fish;

delete a;

→ delete a;는 Animal::~Animal 뿐만 아니라 Fish::~Fish 도 호출함.

 

[ 순수가상함수(Pure Virtual Function) ]

1. 순수 가상 함수

■ 순수 가상 함수 (Pure Virtual Function) 란?

- 선언만 있고 정의가 없는 가상 함수

- 파생 클래스에서 재정의할 것으로 예상되는 함숭에 대해 미리 호출 계획을 세워 두기 위해 정의.

 

■ 가상함수를 순수 가상함수로 만드는 방법

- 다음의 예와 같이 선언시 0을 지정하면 됨.

- 여기서 '=0'은 Pure Specifier임.

class Animal {

  public :

    virtual void breathe() = 0;

};

 

class Fish : public Animal {

  public :

    Fish();

    virtual void breathe();

};

→ Animal::breathe 는 순수 가상함수임. Animal::breathe 는 함수명만 제공하고, 내용은 Fish::breathe 에서 제공하게 됨.

 

2. 추상클래스 (Abstract Class)

■ 추상클래스란?

- 순수 가상함수를 포함하고 있는 클래스.

- 파생클래스의 행동양식을 미리 설계하기 위한 기반클래스임.

- 일반적으로 순수 가상함수를 통해 인터페이스를 제공하고 파생클래스에서 그 인터페이스를 구현하게 됨.

class Animal { // 추상클래스 Animal

  public :

    virtual void breathe() = 0;

};

 

■ 추상클래스의 특징

- 추상클래스의 객체를 생성하는 것은 불가능. ∵ 정의되지 않은 함수를 가지고 있기 때문...

- 추상클래스는 파생클래스를 정의하여 순수가상함수를 구현하였을 때 객체를 생성할 수 있다.

- 추상클래스의 포인터를 선언하는 것은 가능함.

- 추상클래스의 포인터는 흔히 파생 클래스의 객체를 가리키게 되는데, 추상클래스의 포인터가 파생클래스의 객체를 가리킬 때 그 포인터를 통해 가상함수를 호출하면 파생클래스에서 재정의한 함수가 호출됨.

Animal* a = new Fish;

a -> breathe(); // 추상클래스 Animal

delete a;

 

2. 순수 가상함수의 재정의

■ 순수 가상함수의 재정의

- 순수 가상함수는 사용하기 전에 반드시 재정의해야 함.

- 만일 기반 클래스가 순수 가상 함수를 포함하고 있는데 파생 클래스에서 그것을 재정의하지 않는다면 그 파생 클래스도 추상 클래스가 됨.

→ 기반클래스 뿐만 아니라, 그 파생클래스의 객체를 생성하는 것도 불가능 함.

class Animal { // 추상클래스 Animal

  public :

    virtual void breathe() = 0;

};

 

class Fish : public Animal {

  public :

    void swim();

};

 

Animal* a = new Animal; // error

Animal* f = new Fish;  // error

 

출처 : http://blog.daum.net/embedded-seo/449971


반응형