C++ Chapter 12.7 : 순수 가상함수, 추상 클래스, 인터페이스

Date:     Updated:

카테고리:

태그:

인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 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 이렇게 클래스 바깥에서 바디를 구현 해주더라도 소용 없다. 절대 쓰이지 않는다 순수 가상 함수로 한번 선언이 되었다면 반드시 자식 클래스에서 오버라이딩 해야 한다.
  • 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
      • 부모인 인터페이스 타입 하나로 여러 자식들을 참조할 수 있다.
        • 모두 가상 함수이기 때문에 부모인 인터페이스 타입 하나로 자식들이 각자 구현한 함수 내용대로 각각 다르게 호출할 수 있다.
          • 어떤 타입의 자식 객체가 들어오냐에 따라 다른 내용으로 실행됨


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

Cpp 카테고리 내 다른 글 보러가기

댓글 남기기