C++ Chapter 12.7 : 순수 가상함수, 추상 클래스, 인터페이스
카테고리: Cpp
태그: Cpp Programming
인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 C++ 강의를 듣고 정리한 필기입니다. 😀
🌜 [홍정모의 따라 하며 배우는 C++]강의 들으러 가기!
chapter 12. 가상 함수들 : 순수 가상함수, 추상 클래스, 인터페이스
🔔 순수 가상함수
virtual void func() = 0;
순수 가삼 함수
: 함수의 바디가 없는 가상 함수. 즉, 자식 클래스에서 반드시 오버라이딩 해야 하는 함수.
- 그냥 가상 함수는 자식클래스에게 오버라이딩을 권장할 뿐 필수는 아니였었다.
- 그저 자식이 오버라이딩 하면 자식 객체가 오버라이딩 한 바디를 호출하고
- 자식이 오버라이딩 하지 않으면 자기 자신(부모)가 구현 해놓은 가상 함수의 바디를 호출하면 됐었다.
- 순수 가상 함수는 바디를 두고 있지 않기 때문에 자식 클래스에서 반드시 오버라이딩 해야 한다.
- 선언 👉 바디 없이 함수의 프로토타입만 있되 끝에
= 0
을 붙여준다.
- 선언 👉 바디 없이 함수의 프로토타입만 있되 끝에
🔔 추상 기본 클래스
abstract class
: 순수 가상 함수를 포함 하고 있는 클래스.
- 순수 가상 함수를 포함하고 있어 자식 클래스들이 이를 오버라이딩 할 것을 강제한다.
- 추상 클래스는 객체로 찍어낼 수 없다. ⭐
class Animal
{
protected:
string m_name;
public:
Animal(std::string name)
:m_name(name)
{}
public:
string getName() { return m_name; }
virtual void speak() const = 0; // ⭐ 순수 가상 함수
};
class Cow : public Animal
{
protected:
string m_name;
public:
Cow(std::string name)
:Animal(name)
{}
};
void Animal::speak() const
{
cout << m_name << endl;
}
int main()
{
Cow cow("hello"); // ❌에러 발생!❌ 👉 부모의 순수 가상 함수를 오버라이딩 하지 않아서.
Animal ani("Hi"); // ❌에러 발생!❌ 👉 추상 클래스로 객체를 만들어낼 수 없어서.
}
Animal
클래스는 추상 클래스이다.- 순수 가상 함수인 speak 를 가지고 있기 때문에!
- void Animal::speak() const 이렇게 클래스 바깥에서 바디를 구현 해주더라도 소용 없다. 절대 쓰이지 않는다 순수 가상 함수로 한번 선언이 되었다면 반드시 자식 클래스에서 오버라이딩 해야 한다.
- 순수 가상 함수인 speak 를 가지고 있기 때문에!
- Cow cow(“hello”); ❌에러 발생!❌
- 부모 클래스의 순수 가상 함수인 speak()를 오버라이딩 해주지 않은 채로 객체를 생성하려고 하니 에러.
- Animal ani(“Hi”); ❌에러 발생!❌
- 추상 클래스인 Animal의 객체를 만드려고 하니 에러 발생.
- 추상 클래스는 객체로 만들 수 없다.
- 따라서 순수 가상 함수가 아닌 getName()같은 추상 클래스의 일반 멤버 함수들은 접근 지정자에 따라 본인 클래스 내부, 혹은 자식 클래스 내부(protected, public)에서만 사용할 수 있다.
🔔 인터페이스 클래스
class IErrorLog
{
public:
virtual bool reportError(const char * error) = 0;
virtual ~IErrorLog() {}
};
Interface
: 모-든 함수가 순수 가상 함수인 클래스. 본인만의 멤버 변수는 가지지 않는다.
- 인터페이스를 상속 받는 클래스들은 반드시 인터페이스 안에 있는 모든 함수들을 전부 오버라이딩 해야 한다.
- 인터페이스는 순수 가상 함수만 가지고 있기 때문이다.
- 하나라도 빠뜨리면 에러남.
- 인터페이스는 순수 가상 함수만 가지고 있기 때문이다.
- 인터페이스는 본인만의 멤버 변수나 구현된 멤버 함수는 가지지 않는다.
- 인터페이스는 하는 일은 없지만 자식들에게 ‘이런 기능들을 반드시 가지고 있으시고 기능의 내용은 각자 알아서 구현하세요’라는 설계도 같은 역할을 한다.
- 인터페이스도 추상클래스의 한 종류나 마찬가지이기 때문에 인터페이스로 객체로 생성할 수는 없다.
- 인터페이스 클래스의 이름 앞엔 대문자
I
를 붙여주는게 관습이다.
class IErrorLog
{
public:
virtual bool reportError(const char * error) = 0;
virtual ~IErrorLog() {}
};
class FileErrorLog : public IErrorLog
{
public:
bool reportError(const char * errorMessage) override
{
cout << "Writing error to a file" << endl;
return true;
}
};
class ConsoleErrorLog : public IErrorLog
{
public:
bool reportError(const char * errorMessage) override
{
cout << "Printing error to a console" << endl;
return true;
}
};
void doSomething(IErrorLog & log)
{
log.reportError("Runtime error!!");
}
int main()
{
FileErrorLog file_log;
ConsoleErrorLog console_log;
doSomething(file_log); // ⭐다형성
doSomething(console_log); // ⭐다형성
}
- 인터페이스
IErrorLog
를 상속 받는 두 자식 클래스ConsoleErrorLog
,FileErrorLog
.- 인터페이스의 bool reportError(const char * errorMessage) 함수를 각자 오버라이딩 하고 있다.
- 인터페이스에 가상 소멸자를 두어 부모로 자식 객체를 참조하고 있을 때 메모리 해제시 자식들의 소멸자가 호출되게 한다.
- 다형성
- doSomething(file_log); 👉 IErrorLog & log = file_log
- 부모인 인터페이스 타입 하나로 여러 자식들을 참조할 수 있다.
- 모두 가상 함수이기 때문에 부모인 인터페이스 타입 하나로 자식들이 각자 구현한 함수 내용대로 각각 다르게 호출할 수 있다.
- 어떤 타입의 자식 객체가 들어오냐에 따라 다른 내용으로 실행됨
- 모두 가상 함수이기 때문에 부모인 인터페이스 타입 하나로 자식들이 각자 구현한 함수 내용대로 각각 다르게 호출할 수 있다.
- 부모인 인터페이스 타입 하나로 여러 자식들을 참조할 수 있다.
- doSomething(file_log); 👉 IErrorLog & log = file_log
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기