본문 바로가기
C++

[C++]순수 가상함수와 추상 클래스, 인터페이스(interface)

by Junk_Seo 2018. 1. 30.
반응형

순수 가상함수

class 상속에서 virtual 키워드를 통해 오버라이딩 함수(가상함수)를 구현 class 상속의 다형성을 구성했는데, 여기서 문제점이 하나 발생하게 됩니다.

부모 class에서 구현한 가상함수를 자식 class가 반드시 구현해야 한다는 강제성이 없다는 것입니다.

즉, 부모 class에서 가상함수를 구현했지만 자식 class에서 이 가상함수를 오버라이딩 하지 않아도 된다는 것입니다. 이렇게 되면 class 상속을 통해 다형성을 구성하지 못합니다.

이 때문에 등장한 것이 순수 가상함수입니다. 

 

class C_PARENT
{
public:
    virtual void func() abstract;
    //virtual void func() = 0;
    //virtual void func() = NULL;
};
class C_CHILD : public C_PARENT
{
public:
    virtual void func();
};

 

 

순수 가상함수의 형태는 위 코드 파란색 부분과 같이 3가지의 종류가 있습니다. 

 "virtual void func() = 0;"과 "virtual void func() = NULL;"는 abstract 키워드가 나오기 이전에 사용하던 방식입니다. 

abstract 키워드가 나온 이후에는  "virtual void func() abstract; " 방식으로 사용합니다.

순수 가상함수의 경우에는 위 코드처럼 선언만 하고 구현을 하지는 않습니다.

이렇게 부모 class에서 순수 가상함수를 가지면 자식 class는 반드시 오버라이딩한 함수를 멤버로 가져야 하는 강제성을 띄게 됩니다.

 

추상 클래스

이렇게 순수 가상함수를 1개 이상 가지고 있는 class를 추상 클래스라고 합니다.

추상 클래스가 되면 해당 클래스는 인스턴스를 만들 수 없습니다. 

따라서 추상 클래스의 자식 class를 통해 인스턴스를 생성할 수 있게 됩니다.

int main()
{
	C_PARENT* pParent = new C_CHILD();
	pParent->func();
        return 0;
}

위 코드와 같은 형식으로 사용 가능 합니다.

 

abstract 키워드

원래 추상 클래스는 기본 class 형식에 순수 가상함수를 포함하는 것으로 딱히 기본 class와 구분 짓는 방식이 없었습니다. 하지만 이후에 abstract라는 키워드가 생겨나서 이 키워드를 붙여서 추상 클래스라는 것을 구분 짓게 할 수 있게 되었습니다.

class C_PARENT abstract
{
public:
    virtual void func() = 0;
    //virtual void func() = NULL;
};
class C_CHILD : public C_PARENT
{
public:
    virtual void func();
};

위 코드의 빨간색 부분처럼 클래스 명 뒤에 abstract 키워드를 붙여서 추상클래스임을 알려줍니다.

 

인터페이스(__interface)

abstract 키워드의 경우 표준으로 자리 잡았지만 __interface 키워드의 경우에는 아직 표준은 아닙니다.  

__interface C_ANIMAL_INTERFACE
{
	void move();
};

class C_ANIMAL abstract : public C_ANIMAL_INTERFACE
{
...
public:
	...
};
class C_CAT : public C_ANIMAL
{
...
public:
	virtual void move();
};

interface는 위 파란색 코드처럼 사용하며 추상클래스가 이 interface를 상속받아서 사용합니다.

interface의 특징은 다음과 같습니다.

-0개 이상의 기본 인터페이스에서 상속할 수 있습니다.

-기본 클래스에서 상속할 수 없습니다.

-공용 순수 가상 메서드만 포함할 수 있습니다.

-생성자, 소멸자 또는 연산자를 포함할 수 없습니다.

-정적 메서드를 포함할 수 없습니다.

-데이터 멤버를 포함할 수 없습니다. 속성은 허용됩니다.

class는 기본 private인 반면 interface는 public속성을 가지며 함수를 선언하면 특별히 지정하지 않아도 순수 가상함수로 선언됩니다.

 

이러한 interface와 추상 클래스를 통해서 class 상속을 구성합니다.

interface에 순수 가상함수를 구성하고, 이를 추상 클래스가 상속받습니다.

추상클래스는 기본적인 기능을 구현합니다.

이러한 추상클래스를 기본 class들이 상속받고 순수 가상함수를 오버라이딩 합니다.

__interface C_ANIMAL_INTERFACE
{
	void move();
};
class C_ANIMAL abstract : public C_ANIMAL_INTERFACE
{
private:
	int m_nHp;
public:
	C_ANIMAL();
	void setHp(int nHp);
	int getHp();
};
class C_CAT : public C_ANIMAL
{
private:

public:
	virtual void move();
};

위의 코드가 class의 상속을 구성하는 기본 형태입니다.

 

가상 소멸자

상속에서 소멸자에 대한 내용입니다.

추상 클래스를 동적할당으로 자식 class로 인스턴스화한 경우에 발생하는 문제입니다.

__interface C_ANIMAL_INTERFACE
{
	void move();
};

class C_ANIMAL abstract : public C_ANIMAL_INTERFACE
{
private:
	int m_nHp;
public:
	C_ANIMAL();
	~C_ANIMAL();
	void setHp(int nHp);
	int getHp();
};
class C_CAT : public C_ANIMAL
{
private:

public:
	C_CAT();
	~C_CAT();
	virtual void move();
};
int main(void) {
	C_ANIMAL* pAnimal = nullptr;
	pAnimal = new C_CAT();

	delete pAnimal;
	pAnimal = nullptr;
	return 0;
}

추상 클래스와 이를 상속받는 class의 생성자와 소멸자에서 단순히 출력문 내용을 넣은 뒤 동적할당과 메모리 해제를 하는 코드입니다. 

이를 실행하면 다음과 같이 나옵니다.

위의 출력문처럼 자식 class의 소멸자가 호출되지 않는 것을 볼 수 있습니다.

즉, 동적 할당한 부모 class를 메모리 해제 하면서 자식 class의 소멸자를 호출해야 하는데 이것이 안되고 단순히 부모 class의 소멸자만 호출되고 있습니다.

이 문제를 해결하기 위해서 소멸자에 virtual 키워드를 추가하여 가상 소멸자를 만들어야 합니다.

__interface C_ANIMAL_INTERFACE
{
	void move();
};

class C_ANIMAL abstract : public C_ANIMAL_INTERFACE
{
private:
	int m_nHp;
public:
	C_ANIMAL();
	virtual ~C_ANIMAL();
	void setHp(int nHp);
	int getHp();
};
class C_CAT : public C_ANIMAL
{
private:

public:
	C_CAT();
	virtual ~C_CAT();
	virtual void move();
};
int main(void) {
	C_ANIMAL* pAnimal = nullptr;
	pAnimal = new C_CAT();

	delete pAnimal;
	pAnimal = nullptr;
	return 0;
}

위 코드처럼 소멸자에 virtual 키워드를 추가하여 가상 소멸자 형태를 취하면 부모 class가 소멸자를 호출하면서 자식 class의 소멸자를 호출하게 하여 문제를 해결하게 됩니다.

출력문을 보면 자식 소멸자도 호출하고 있는 것을 알 수 있습니다.

 

 

****

<참고>

virtual 키워드의 경우 상속관계가 아닌 상황에서 사용하게 되면 성능저하가 있을 수 있기 때문에 상속관계가 아니라면 사용하지 않습니다.

 

반응형

'C++' 카테고리의 다른 글

[C++]friend 키워드  (0) 2018.02.21
[C++]Class의 기본 형태  (0) 2018.01.30
[C++]Class 상속  (0) 2018.01.30
[C++]Class Operator와 대입연산자  (0) 2018.01.25
[C++]Class 소멸자(Destructor)  (0) 2018.01.25