<개인공부>/[C++]

[C++] override, final 키워드 (가상함수 관련 키워드)에 대해서

BlockDMask 2020. 8. 17. 13:53
반응형

안녕하세요. BlockDMask 입니다. 오늘은 가상함수 virtual, 상속 주제와 관련해서 사용할 수 있는 override, final 키워드를 가지고 왔습니다. override, final 둘 다 C++11 이후 부터 사용이 가능한 키워드 이며 개발하면서 상속과 virtual 이 나오면 거의 무조건 사용하게 되는 키워드 입니다. 그럼 시작해보겠습니다.

<목차>

  1. override, final 알기 전에 필요한 지식들.

    1-1) 상속에 대해서

    1-2) virtual 이라는 키워드

  1. override 키워드에 대해서

    2-1) override 란?

    2-2) override 사용 예시

  1. final 키워드에 대해서

    3-1) final 이란?

    3-2) final 키워드 사용 예시 - 클래스

    3-3) final 키워드 사용 예시 - 멤버함수


1. final과 override 키워드를 배우기 전에 ...


1-1) 상속

상속이란 어떤 A라는 클래스의 함수 혹은 매개변수들을 B라는 클래스에서도 사용할 수 있도록 하거나, B라는 클래스가 A라는 클래스의 기능을 포함해서 더 확장성 있게 클래스를 정의 하기 위해서 사용하는 개념(?) 입니다.

제 블로그에는 상속에 대해서 아직 적은게 없네요. 나중에 정리해서 포스팅 하도록 하겠습니다.


2) virtual 이라는 키워드(가상함수와 순수가상함수)

간단하게 이야기 해보자면 virtual 이라는 키워드는 상속 받은 자식 클래스에서 해당 함수를 오버라이딩 해서 사용하라는 것을 알려주는 함수 입니다.

그냥 가상함수는 오버라이딩을 강제하지 않고 옵션으로 두지만, 경우에 따라서는 순수 가상함수를 선언하여 자식 클래스에서 '무조건' 오버라이딩 하도록 강제할 수도 있습니다.

가상함수 ⇒ 오버라이딩 옵션 (해도 ok, 안해도 ok)

순수가상함수 ⇒ 오버라이딩 필수 (안하면 응 에러~)

가상함수와 순수 가상함수에 대해서 더 자세한 것은 제 블로그에 존재합니다. [바로가기]


2. override 키워드에 관해서


2-1) override 란?

오버라이드 키워드를 이용하여 오버라이딩 함수라는 것을 명시하여, 사람(=프로그래머)이 할 수 있는 실수를 컴파일러가 다룰 수 있도록 합니다.

부모 클래스의 함수를 그대로 잘 가지고 와서 오버라이딩을 정확하게 해야하는데 우리가 사람이다 보니 매개변수가 다르거나 하는 실수 가 있을 수 있습니다. override 키워드를 붙여주면 해당 함수가 상속 받아서 오버라이딩이 가능한 함수인지, 그 함수의 이름과 매개 변수등이 잘 맞는지를 컴파일러 보고 확인 하라고 떠넘길 수 있습니다. 또한, 명확하게 이게 상속 받은 함수인지 눈으로 확인할 수 있습니다.

Effective Mordern C++ 책을 보면 재 정의(=오버라이딩)의 필수조건을 아래와 같이 나열하고 있습니다.

  • 기반 클래스 함수가 반드시 가상 함수 이어야한다.
  • 기반 함수와 파생 함수의 이름이 반드시 동일해야한다. (소멸자 예외)
  • 기반 함수와 파생함수의 매개변수 형식들이 반드시 동일해야한다.
  • 기반함수와 파생함수의 const 성이 반드시 동일해야한다.
  • 기반함수와 파생함수의 반환형식과 예외 명세가 호환되어야한다.
  • 멤버 함수들의 참조 한정자(reference qualifier)가 동일해야한다. (C++11 추가)

2-2) override 예제

class Parent {
public:
		virtual void func1() const;
		virtual void func2(int num);
		virtual void func3() &;
		virtual void func4();
	  virtual void func5() = 0;
    void func6();
};

class Child: public Parent {
    // 부모클래스의 func1 함수와 const성이 같지 않기 때문에 오버라이딩 불가능
    // virtual void func1() override;
		virtual void func1() const override;    // 이렇게 같아야함


    // 부모클래스의 func2 함수와 매개변수가 다르므로 오버라이딩 불가능
    //virtual void func2(int num, int num2) override;
    virtual void func2(int num) override;   // 이렇게 같아야함


    // 참조 한정자
		virtual void func3() & override;

	
    // virtual 키워드를 생략해도 오버라이딩 가능
    void func4() override;           // 가능
    //virtual void func4() override; // 가능


    // 순수 가상함수
    virtual void func5() override;


    // 가상함수가 아닌 함수는 오버라이딩이 불가능
    //virtual void func6() override;
};

"야 컴파일러 이거 내가 자식 클래스에서 override 한거다? 이거 썼으니까 너가 부모클래스에 이 함수 똑같은거 있는지 알아와. 이제 니 책임이야." 이런 뜻으로 받아들였습니다. 프로그래머가 실수할 수 있는 부분을 컴파일러에게 책임을 떠넘겨서 프로그래머가 실수할 수 있는 여지를 없애는 것 입니다.

func4번 예제를 보면, override할때 virtual 키워드를 붙이지 않았습니다. virtual 키워드는 생략이 가능합니다. 즉, override 할때 virtual은 붙여도되고 붙이지 않아도 됩니다. (저는 개인적으로 붙이는걸 선호합니다.)

3. final 키워드에 관해서


3-1) final 이란?

상속을 방지하는 키워드 입니다. 즉, 더이상 가상함수를 오버라이딩 하지 않겠다는 뜻힙니다.

클래스와 메서드(멤버 함수) 둘다 사용이 가능합니다.

부모 클래스의 특정 멤버 함수를 자식 클래스에서 재정의 하지 못하도록 막을때 사용한다거나, 부모 클래스를 자식 클래스에 상속 받을때 클래스 자체를 더이상 상속이 불가능하게 하려고할때 쓰입니다.

final 키워드가 붙은 함수를 상속 받아서 사용하려 할때는 컴파일 에러가 발생합니다.

> 클래스를 더이상 상속 불가능한 클래스로 만들때, final 키워드를 사용합니다.

보통 상속 불가능한 클래스를 만들때는 아래와 같이 만듭니다.

"[클래스이름] final {};" 으로 합니다.

class Car final { ... };

상속과 같이 쓰이는 경우에는 아래와 같은 모양입니다.

"[클래스이름] final : public [부모클래스이름] {};" 이런식.

class Child final : public Parent { ... };

> final 키워드를 멤버 함수에서 사용할때

멤버함수 이름 끝 부분에 나오게 됩니다.

void func() final;

이 함수는 부모 클래스에서 virtual 로 선언된 가상 함수 이어야만 final 키워드를 사용할 수 있습니다. 즉 상속 받아서 오버라이딩(=재정의)가 가능한 함수를 사용할때만 final을 사용할 수 있습니다.

즉, 더이상 상속할수 없음(=더이상 오버라이딩 할수없음)을 표현하는 것 입니다. "여기가 마지노선 이니 여기서 마지막으로 오버라이딩을 하고 그 이하는 더이상 금지 퉤퉤퉤" 이런느낌입니다.


3-2) final 키워드 예시 - 클래스에 사용할 때

// 클래스에 final 키워드가 있는 경우
class SuperCar final {
public:
		void func1() const;
    void func2() const;
};

//class NormalCar : SuperCar{ }; // 불가능

이런식으로 나오게 됩니다.

위 캡쳐에서 처럼 final 로 선언된 클래스는 더이상 상속의 부모 클래스로 사용할 수 없습니다.

3-2) final 키워드 예시 - 멤버 함수에 사용할 때

class Parent {
public:
	virtual void func1();
    virtual void func2() const final;
    
    //비 가상 멤버함수는 final 키워드를 사용할 수 없습니다.
    //void func3() final;
};

class Child : Parent {
//func1 멤버함수는 여기를 기점으로 final 선언 virtual void func1() final; //func2 함수는 SuperCar 클래스에서 final 선언을 하였기 떄문에 오버라이딩 불가능 //virtual void func2() const override; }; class MyChild : Child { //func1 멤버함수는 Car 클래스에서 final 선언을 하였기 때문에 오버라이딩 불가능 //virtual void func1() override; };

final을 이용해서 멤버함수에 선언하면 본인 클래스에서 까지는 오버라이딩이 가능하지만, 본인을 상속한 본인 아래의 클래스에서는 오버라이딩이 불가능 한 것을 볼 수 있습니다.

이상으로 C++11 override, final 키워드에 대해서 알아보았습니다. 감사합니다.

반응형