구현 된 순수 가상 기능


176

내 기본 이해는 순수한 가상 기능에 대한 구현이 없다는 것입니다. 그러나 순수한 가상 기능에 대한 구현이있을 수 있다고 들었습니다.

class A {
public:
    virtual void f() = 0;
};

void A::f() {
    cout<<"Test"<<endl;
}

위의 코드가 정상입니까?

구현으로 순수한 가상 기능을 만드는 목적은 무엇입니까?

답변:


215

순수 virtual함수는 직접 인스턴스화 할 파생 유형으로 구현해야하지만 기본 유형은 여전히 ​​구현을 정의 할 수 있습니다. (호출하여 완벽한 범위의 이름을 사용하여 (액세스 권한을 허용하는 경우) 파생 클래스는 명시 적으로 기본 클래스 구현을 호출 할 수 있습니다 A::f()- 경우의 예에서 A::f()있었다 public또는 protected). 다음과 같은 것 :

class B : public A {

    virtual void f() {
        // class B doesn't have anything special to do for f()
        //  so we'll call A's

        // note that A's declaration of f() would have to be public 
        //  or protected to avoid a compile time problem

        A::f();
    }

};

내가 생각할 수있는 유스 케이스는 다소 합리적인 기본 동작이있을 때이지만 클래스 디자이너는 기본 정렬 동작이 명시 적으로 만 호출되기를 원합니다. 파생 클래스가 항상 자체 작업을 수행하지만 공통된 기능 집합을 호출 할 수도 있습니다.

언어에 의해 허용되지만, 내가 일반적으로 사용하는 것으로 보이는 것은 아닙니다 (그리고 그것을 할 수 있다는 사실은 대부분의 C ++ 프로그래머, 심지어 경험이 많은 프로그래머를 놀라게하는 것처럼 보입니다).


1
프로그래머에게 놀라운 이유를 추가하는 것을 잊었습니다. 인라인 정의가 표준에 의해 금지되어 있기 때문입니다. 순수 가상 메소드 정의는이어야합니다 deported. (.inl 또는 .cpp에서 일반적인 파일 이름 지정 방법을 나타냅니다).
v.oddou

이 호출 메소드는 정적 메소드 멤버 호출과 동일합니다. Java의 일종의 클래스 메소드.
Sany Liew

2
"일반적으로 사용되지 않음"== 나쁜 습관? 나는 NVI를 구현하려고 시도하는 것과 똑같은 행동을 찾고있었습니다. 그리고 NVI는 저에게 좋은 습관 인 것 같습니다.
Saskia

5
A :: f ()를 순수하게 만드는 것은 B f ()를 구현 해야 한다는 것을 의미합니다 (그렇지 않으면 B는 추상적이고 인스턴스화 할 수 없습니다). @MichaelBurr가 지적했듯이 A :: f ()에 대한 구현을 제공한다는 것은 B가이를 사용하여 f ()를 정의 할 수 있음을 의미합니다 .
fearless_fool

2
IIRC, Scot Meyer는 그의 고전 저서 "더 효과적인 C ++"중 하나에서이 질문의 사용 사례에 대한 훌륭한 기사를 가지고 있습니다
irsis

75

분명히, 당신은 무엇을 오해하고 있습니다 = 0; 가상 기능 후 의미합니다.

= 0은 파생 클래스가 구현을 제공해야한다는 것을 의미하며 기본 클래스가 구현을 제공 할 수는 없습니다.

실제로 가상 함수를 순수 (= 0)로 표시하면 누군가가 Base :: Function (...)을 통해 명시 적으로 수행하지 않는 한 또는 호출되지 않기 때문에 정의를 제공 할 때 아무런 의미가 없습니다. 기본 클래스 생성자는 해당 가상 함수를 호출합니다.


9
이것은 올바르지 않습니다. 순수한 가상 클래스의 생성자에서 순수한 가상 함수를 호출하면 순수한 가상 호출이 수행됩니다. 어떤 경우에는 구현이 더 좋습니다.
rmn

@rmn, 그렇습니다. 생성자의 가상 호출에 대한 것입니다. 나는 대답을 업데이트했다. 그래도 모든 사람들이 그렇게하지 않기를 바랍니다. :)
Terry Mahaffey

3
실제로 생성자에서 기본 순수 호출을 수행하면 구현 정의 동작이 발생합니다. VC ++에서는 _purecall 충돌이 발생합니다.
Ofek Shilon 2016 년

@OfekShilon 맞습니다-정의되지 않은 동작과 나쁜 연습 / 코드 리팩터링 (생성자 내부의 가상 메소드 호출)의 후보라고도 할 수 있습니다. 가상 테이블 일관성과 관련이 있으며 올바른 구현 본문으로 라우팅 할 준비가되지 않았을 수 있습니다.
teodron

1
생성자와 소멸자에서 가상 함수는 가상 이 아닙니다 .
Jesper Juhl

20

이 방법의 장점은 파생 형식이 여전히 메서드를 재정의하지만 기본 또는 추가 구현을 제공한다는 것입니다.


1
기본 구현이있는 경우 왜 강제하고 싶습니까? 일반적인 가상 기능처럼 들립니다. 그것이 단지 정상적인 가상 함수라면, 재정의 할 수 있고 그렇지 않은 경우 기본 구현이 제공됩니다 (기본 구현).
StackExchange123

19

파생 클래스에서 실행해야하는 코드가 있지만 직접 실행하지 않으려는 경우 강제로 재정의하려는 경우

코드는 정확하지만 모든 것이 자주 사용되는 기능은 아니지만 일반적으로 순수한 가상 소멸자를 정의하려고 할 때만 나타납니다.이 경우 구현을 제공 해야 합니다. 재미있는 점은 일단 그 클래스에서 파생되면 소멸자를 재정의 할 필요가 없다는 것입니다.

따라서 순수한 가상 함수의 현명한 사용법은 순수한 가상 소멸자를 "최종"키워드로 지정하는 것입니다.

다음 코드는 놀랍도록 정확합니다.

class Base {
public:
  virtual ~Base() = 0;
};

Base::~Base() {}

class Derived : public Base {};

int main() { 
  // Base b; -- compile error
  Derived d; 
}

1
기본 클래스 소멸자는 항상 어쨌든 가상이든 아니든 순수하거나 불리거나 불려집니다. 다른 함수를 사용하면 기본 클래스 버전이 순수한지 여부에 관계없이 재정의 가상 함수가 기본 클래스 구현을 호출한다고 보장 할 수 없습니다.
CB Bailey

1
그 코드가 잘못되었습니다. 언어의 구문 문제로 인해 클래스 정의 외부에서 dtor를 정의해야합니다.

@Roger : 고맙습니다. 실제로 도움이되었습니다. 이것은 내가 사용하고있는 코드이며 MSVC에서 잘 컴파일되지만 이식성이 없을 것 같습니다.
Kornel Kisielewicz


4

네 맞습니다. 예제에서 A에서 파생 된 클래스는 인터페이스 f ()와 기본 구현을 모두 상속합니다. 그러나 파생 클래스가 f () 메소드를 구현하도록 강제합니다 (A에서 제공하는 기본 구현 만 호출하더라도).

Scott Meyers는 Effective C ++ (제 2 판) 항목 # 36에서이를 설명합니다. 인터페이스 상속과 구현 상속을 구분합니다. 최신 버전에서 품목 번호가 변경되었을 수 있습니다.


4

본문이 있거나없는 순수한 가상 함수는 파생 된 형식이 자체 구현을 제공해야 함을 의미합니다.

기본 클래스의 순수 가상 함수 본문은 파생 클래스가 기본 클래스 구현을 호출하려는 경우 유용합니다.


2

'가상 무효 foo () = 0;' 구문은 현재 클래스에서 foo ()를 구현할 수 없다는 것을 의미하지는 않습니다. 또한 파생 클래스에서 구현해야한다는 의미는 아닙니다 . 당신이 나를 때리기 전에, 다이아몬드 문제를 관찰 해 봅시다 :

class A
{
public: 
    virtual void foo()=0;
    virtual void bar();
}

class B : public virtual A
{
public:
    void foo() { bar(); }
}

class C : public virtual A
{
public:
    void bar();
}

class D : public B, public C
{}

int main(int argc, const char* argv[])
{
    A* obj = new D();
    **obj->foo();**
    return 0;
}

이제 obj-> foo () 호출로 B :: foo () 및 C :: bar ()가 발생합니다.

알다시피 ... 순수 가상 메소드는 파생 클래스에서 구현할 필요가 없습니다 (foo ()는 C 클래스에서 구현되지 않습니다-컴파일러는 컴파일됩니다) C ++에는 많은 허점이 있습니다.

내가 도울 수 있기를 바랍니다 :-)


5
모든 파생 클래스에서 구현할 필요는 없지만 인스턴스화하려는 모든 파생 클래스에서 구현해야합니다. C예제에서 유형의 객체를 인스턴스화 할 수 없습니다 . 에서 D구현을 가져 foo오기 때문에 유형의 객체를 인스턴스화 할 수 있습니다 B.
YoungJohn

0

구현 본문과 함께 순수한 가상 메소드 를 사용하는 한 가지 중요한 유스 케이스 는 추상 클래스를 원하지만 클래스에 순수 가상으로 만들 적절한 메소드가없는 것입니다. 이 경우 클래스의 소멸자를 순수 가상으로 만들고 원하는 구현 (빈 바디)을 넣을 수 있습니다. 예로서:

class Foo
{
   virtual ~Foo() = 0;
   void bar1() {}
   void bar2(int x) {}
   // other methods
};

Foo::~Foo()
{
}

이 기술은 Foo클래스를 추상화하여 결과적으로 클래스를 직접 인스턴스화 할 수 없습니다. 동시에 Foo클래스 추상화 를위한 추가 순수 가상 메소드를 추가하지 않았습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.