수색…


통사론

  • 가상 void f ();

  • 가상 공극 g () = 0;

  • // C ++ 11 이상 :

    • virtual void h () override;
    • void i () override;
    • 가상 void j () final;
    • void k () final;

비고

C ++ 11 이상에서 가상으로 override 사용

지정자 override 는, C ++ 11 이후로, 함수의 시그 니챠의 마지막에 추가하는 경우, 특별한 의미를가집니다. 이것은 함수가

  • 기본 클래스 &
  • 기본 클래스 함수는 virtual

이 지시자의 run time 중요성은 컴파일러에 대한 지시 사항을 의미하지 않는다.

아래의 예는 오버라이드를 사용하지 않고 행동 변화를 보여줍니다.

override 없이 :

#include <iostream>

struct X {
    virtual void f() { std::cout << "X::f()\n"; }
};

struct Y : X {
    // Y::f() will not override X::f() because it has a different signature,
    // but the compiler will accept the code (and silently ignore Y::f()).
    virtual void f(int a) { std::cout << a << "\n"; }
};

override :

#include <iostream>

struct X {
    virtual void f() { std::cout << "X::f()\n"; }
};

struct Y : X {
    // The compiler will alert you to the fact that Y::f() does not
    // actually override anything.
    virtual void f(int a) override { std::cout << a << "\n"; }
};

override 는 키워드가 아니라 함수 시그니처에만 나타날 수있는 특수 식별자입니다. 다른 모든 컨텍스트에서는 여전히 override 식별자로 사용할 수 있습니다.

void foo() {
    int override = 1; // OK.
    int virtual = 2;  // Compilation error: keywords can't be used as identifiers.
}

가상 대 비가 상 회원 기능

가상 멤버 함수 사용 :

#include <iostream>

struct X {
    virtual void f() { std::cout << "X::f()\n"; }
};

struct Y : X {
    // Specifying virtual again here is optional
    // because it can be inferred from X::f().
    virtual void f() { std::cout << "Y::f()\n"; } 
};

void call(X& a) {
    a.f();
}

int main() {
    X x;
    Y y;
    call(x); // outputs "X::f()"
    call(y); // outputs "Y::f()"
}

가상 멤버 함수가없는 경우 :

#include <iostream>

struct X {
   void f() { std::cout << "X::f()\n"; }
};

struct Y : X {
   void f() { std::cout << "Y::f()\n"; }
};

void call(X& a) {
    a.f();
}

int main() {
    X x;
    Y y;
    call(x); // outputs "X::f()"
    call(y); // outputs "X::f()"
}

최종 가상 함수

C ++ 11에서는 메서드 시그니처에 메서드가있는 경우 메서드 재정의를 금지하는 final 지정자가 도입되었습니다.

class Base {
public:
    virtual void foo() {
        std::cout << "Base::Foo\n";
    }
};

class Derived1 : public Base {
public:
    // Overriding Base::foo
    void foo() final {
        std::cout << "Derived1::Foo\n";
    }
};

class Derived2 : public Derived1 {
public:
    // Compilation error: cannot override final method
    virtual void foo() {
        std::cout << "Derived2::Foo\n";
    }
};

지정자 final 은`virtual '멤버 함수에만 사용할 수 있으며 비 가상 멤버 함수에는 적용 할 수 없습니다

final 과 마찬가지로 파생 클래스에서 virtual 함수의 재정의를 방지하는 지정자 호출자 'override'도 있습니다.

지정자를 override 하고 final 함께 결합하여 원하는 효과를 얻을 수 있습니다.

class Derived1 : public Base {
public:
    void foo() final override {
        std::cout << "Derived1::Foo\n";
    }
};

생성자와 소멸자의 가상 함수 동작

생성자와 소멸자의 가상 함수의 동작은 처음 만났을 때 종종 혼란 스럽습니다.

#include <iostream>
using namespace std;

class base { 
public:
    base() { f("base constructor"); }
    ~base() { f("base destructor"); }

    virtual const char* v() { return "base::v()"; }

    void f(const char* caller) { 
        cout << "When called from " << caller << ", "  << v() << " gets called.\n"; 
    }        
};

class derived : public base {
public:
    derived() { f("derived constructor"); }
    ~derived() { f("derived destructor"); }

    const char* v() override { return "derived::v()"; }

};

int main() {
     derived d;
}

산출:

기본 생성자에서 호출되면 base :: v ()가 호출됩니다.
파생 생성자에서 호출되면 derived :: v ()가 호출됩니다.
파생 된 소멸자에서 호출되면 derived :: v ()가 호출됩니다.
기본 소멸자에서 호출되면 base :: v ()가 호출됩니다.

그 이유는 파생 클래스가 아직 초기화되지 않았거나 (생성자의 경우) 또는 이미 소멸 된 경우 (소멸자의 경우) 추가 구성원을 정의 할 수 있으며 구성원 함수를 호출하는 것이 안전하지 않을 수 있기 때문입니다. 따라서 C ++ 객체의 생성 및 제거 중에 동적 유형 인 *this 는 생성자 또는 소멸자의 클래스로 간주되며 파생 클래스가 아닌 것으로 간주됩니다.

예:

#include <iostream>
#include <memory>

using namespace std;
class base {
public:
    base()
    {
        std::cout << "foo is " << foo() << std::endl;
    }
    virtual int foo() { return 42; }
};

class derived : public base {
    unique_ptr<int> ptr_;
public:
    derived(int i) : ptr_(new int(i*i)) { }
    // The following cannot be called before derived::derived due to how C++ behaves, 
    // if it was possible... Kaboom!
    int foo() override   { return *ptr_; } 
};

int main() {
    derived d(4);
}

순수 가상 함수

virtual 함수가 선언에 = 0 을 추가하여 순수 가상 (추상)임을 지정할 수도 있습니다. 하나 이상의 순수 가상 함수가있는 클래스는 추상 클래스로 간주되므로 인스턴스화 할 수 없습니다. 모든 순수 가상 함수를 정의하거나 상속 한 파생 클래스 만 인스턴스화 할 수 있습니다.

struct Abstract {
    virtual void f() = 0;
};

struct Concrete {
    void f() override {}
};

Abstract a; // Error.
Concrete c; // Good.

함수가 순수 가상으로 지정된 경우에도 기본 구현을 제공 할 수 있습니다. 그럼에도 불구하고이 함수는 여전히 추상화 된 것으로 간주되며 파생 클래스는 인스턴스화되기 전에 함수를 정의해야합니다. 이 경우 파생 클래스 '버전의 함수는 기본 클래스'버전을 호출 할 수 있습니다.

struct DefaultAbstract {
    virtual void f() = 0;
};
void DefaultAbstract::f() {}

struct WhyWouldWeDoThis : DefaultAbstract {
    void f() override { DefaultAbstract::f(); }
};

우리가 이것을 원할 수있는 몇 가지 이유가 있습니다 :

  • 자체적으로 인스턴스화 할 수없는 클래스를 만들지 만 파생 클래스가 인스턴스화되는 것을 막지 않으면 소멸자를 순수 가상으로 선언 할 수 있습니다. 소멸자이기 때문에 인스턴스를 할당 해제 할 수 있도록하려면 반드시 정의해야합니다. 소멸자가 다형성 사용 중에 메모리 누수를 방지하기 위해 이미 가상 일 가능성이 높으므로 다른 함수 virtual 을 선언 할 때 불필요한 성능 저하가 발생하지는 않습니다. 이것은 인터페이스를 만들 때 유용 할 수 있습니다.

      struct Interface {
          virtual ~Interface() = 0;
      };
      Interface::~Interface() = default;
    
      struct Implementation : Interface {};
      // ~Implementation() is automatically defined by the compiler if not explicitly
      //  specified, meeting the "must be defined before instantiation" requirement.
    
  • 순수 가상 함수의 대부분 또는 모든 구현에 중복 코드가 포함되는 경우 해당 코드를 기본 클래스 버전으로 이동하여 코드를 더 쉽게 유지 관리 할 수 ​​있습니다.

      class SharedBase {
          State my_state;
          std::unique_ptr<Helper> my_helper;
          // ...
    
        public:
          virtual void config(const Context& cont) = 0;
          // ...
      };
      /* virtual */ void SharedBase::config(const Context& cont) {
          my_helper = new Helper(my_state, cont.relevant_field);
          do_this();
          and_that();
      }
    
      class OneImplementation : public SharedBase {
          int i;
          // ...
    
        public:
          void config(const Context& cont) override;
          // ...
      };
      void OneImplementation::config(const Context& cont) /* override */ {
          my_state = { cont.some_field, cont.another_field, i };
          SharedBase::config(cont);
          my_unique_setup();
      };
    
      // And so on, for other classes derived from SharedBase.
    


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow