2010-03-11 4 views
5

가상 함수의 구현에 대해 많이 이야기한다고 생각합니다. 제 질문은 순수 가상 기능은 무엇입니까? 그러나 그것은 구현됩니까? 가상 테이블에서 순수 또는 비 순수이라고 말하는 법? 순수 가상 함수와 구현 가상 함수의 차이점은 무엇입니까?C++에서 순수 가상 함수를 구현하는 방법

+1

http://stackoverflow.com/questions/2156634/why-pure-virtual-function-is-initialized-by-0 –

답변

4

일반적으로 순수 가상 기능과 비 순수 가상 기능 간의 구현에는 차이가 없습니다. 순수 가상 함수가 정의되면 다른 가상 함수처럼 작동합니다. 정의되어 있지 않으면 명시 적으로 호출 된 경우에만 문제가 발생합니다.

동작에는 크게 두 가지 차이점이 있지만 일반적으로 가상 기능 메커니즘 자체의 구현에는 영향을주지 않습니다. 컴파일러는 상속 계층 구조에서 비 순수 최종 재 지정을 가지지 않는 순수 가상 함수를 가진 유형의 객체를 생성하도록 허용해서는 안되며 순수 가상 함수에 대한 가상 호출을 직접 또는 간접적으로 객체의 생성자 또는 소멸자는 정의되지 않은 동작을 발생시킵니다.

+0

가상으로 호출되는 경우 정의되지 않은 동작입니다. 함수가 구현되어 있으면'AbstractBase :: Foo()'형식으로 호출 할 수 있습니다. –

+0

정의되지 않은 경우 명시 적으로 호출하려고하면 링커 오류가 발생하는 경향이 있습니까? -하지만 추상 형식의 인스턴스를 가질 수 없다면 어떻게 사실상 호출 할 수 있습니까? – UncleBens

+0

@UncleBens : 잘 모르겠다. 나는 표준의 10.4 부분을 그냥 의치 챘다. –

0

실제 구현은 잘 모르지만 vtableNULL 포인터로 구현하는 것이 좋습니다. 즉, 구현이있는 경우 vtable에 유효한 함수 포인터가 있고 순수 가상 인 경우 NULL 포인터가 있습니다.

이것은 매우 논리적이므로 나는 이라고 생각합니다 .--).

+0

하지만 순수 가상 함수는 여전히 구현이 가능합니까? – skydoor

+0

순수 가상 함수는이를 선언 한 클래스에서 구현을 가질 수 없습니다. 그렇지 않으면 정의에 의해 순수 가상 함수가 아닙니다. 파생 클래스에서 구현되어야합니다. – codenheim

+2

@skydoor : 예, 가능하지만 구현을 구체적으로 호출해야합니다. 여전히 선언 된 객체 (및 순수 가상을 덮어 쓰지 않는 모든 하위 클래스)가있는 클래스를 차단합니다. –

3

가상 공백 foo() = 0;

순수 가상 함수는 여전히 가상 함수이므로 vtable에있을 수 있지만 컴파일러에는 구현이 필요하지 않으며 순수 가상을 선언하는 기본 클래스의 인스턴스화를 금지합니다. 추상 기본 클래스 유형의 포인터를 역 참조 할 수 있기 때문에 가상 테이블에는 다형성 및 런타임 바인딩을위한 항목이 있어야합니다.

도움이 되었습니까?

+1

순수 가상 함수를 선언하면 vtable의 함수에 대한 항목이 추가됩니다. 순수 가상 함수 (및 재정의하지 않는 파생 클래스)를 선언하는 클래스에서 대부분의 구현은이 항목을 설정하여 일부 오류 메시지를 표시하는 일종의 진단 함수를 호출합니다 (예 : "오류 : 순수 가상 함수 호출 "). 순수 가상 함수를 정의 할 수는 있지만 사실상 호출 할 수는 없습니다. –

+0

아, 맞습니다. 감사. 순수 가상 함수는 기본 클래스에 정의를 가질 수 있습니다. +1하여 수정하십시오. – codenheim

2

이 답이 아니라, A는 C++ 언어는 가상 파견 메커니즘이 건설 중에 일어나는 방법을 정의 this answer above

로 의견을 후속. 계층 구조에서 객체를 인스턴스화 할 때 기본 생성자가 호출됩니다 (*). 이 시점에서 가상 디스패치 메커니즘은 기본 클래스에 대해 초기화됩니다. 이 단계에서 가상 함수가 기본 구현에 전달됩니다. 가상 디스패치 메커니즘 (명시 적 클래스 자격없이)을 사용하는 비 순수 가상 메서드에 대한 호출은 기본 구현을 호출합니다. 베이스 생성자가 완료된 후

가상 송출기구 (일반적 VTABLE)는 유도 형 버전 reseted 얻는다 거기에서 동적 호출의 방법의 도출 버전 호출 :

struct base { 
    virtual void non_pure() { std::cout << "base::non_pure" << std::endl; } 
    virtual void pure_not_implemented() = 0; 
    virtual void pure_implemented() = 0; 

    base() {      // at this point the object is a ´base´ 
     non_pure();    // base::non_pure 
     // pure_not_implemented(); // runtime error: pure virtual method called 
     pure_implemented();  // base::pure_implemented 
     // base::pure_not_implemented(); // link error 
    } 
}; 
void base::pure_implemented() { std::cout << "base::pure_implemented" << std::endl; } 
struct derived : base { 
    virtual void non_pure() { std::cout << "derived::non_pure" << std::endl; } 
    virtual void pure_not_implemented() { std::cout << "derived::pure_not_implemented" << std::endl; } 
    virtual void pure_implemented() { std::cout << "derived::pure_implemented" << std::endl; 

    derived() {    // after the implicit call to the base class 
           // this is a ´derived´ object, now calls will 
           // get dispatched to derived:: implementations 
     non_pure();    // derived::non_pure 
     pure_not_implemented(); // derived::pure_not_implemented 
     pure_implemented();  // derived::pure_implemented 
     base::non_pure();  // base::non_pure 
     // base::pure_not_implemented() // link error 
    } 
}; 

을 동적 디스패치 메커니즘 (일반적으로 vtable)과 특정 메서드를 정규화 된 이름으로 호출하는 것 사이에는 차이점이 있습니다. 전체 자격을 통해 구현되지 않은 순수 가상 메서드에 대한 호출은 컴파일 타임에 허용되지만 링크시에는 실패합니다 (링커는 호출 할 구현을 찾을 수 없습니다).

이것은 언어 디자인에서 중요한 결정입니다. 다른 옵션 (자바가 취한)은 기본 클래스 생성자를 호출하기 전에 처음부터 가장 파생 된 유형으로 가상 디스패치 메커니즘을 초기화하는 것입니다.이 접근법의 문제점은 기본 생성자가 가상 ​​메소드 (Java에서 모두 해당)를 호출하면 가장 파생 된 구현에 전달되어 아직 생성되지 않은 객체에서 실행되어 예상치 못한 결과가 발생할 수 있다는 것입니다.

public class Base 
{ 
    public Base() { 
     f(); 
    } 
    public void f() { 
     System.out.println("Base.f"); 
    } 
} 
public class Derived extends Base { 
    public final int constant; 
    public Derived() { constant = 5; } 
    public void f() { 
     System.out.println("Derived.f() " + constant); 
    } 
    public static void main(String args[]) { 
     Derived d = new Derived(); // prints Derived.f() 0 
    } 
} 

동적 버전 관리 메커니즘은 객체가 처음부터 Derived 유형으로 간주합니다. 기본 생성자에서 f()에 대한 호출이 파생 된 구현에 동적으로 전달됩니다. 위의 예제에서 변수가 final로 선언되었으므로 값이 5 인 상수 (코드에서 명확하게 나타남) 인 경우에도 Derived의 생성자가 실행되지 않았기 때문에 실제 인쇄 된 값은 0입니다.

(*) 이것은 지나치게 단순화되어 있지만 세부 사항은 실제로 인수에 영향을주지 않습니다.

+0

생성자에서 순수 가상을 호출하는 것이 정의되지 않은 동작이되는 이유가 궁금했지만 순수하지 않은 호출은되지 않습니다. 특히, 컴파일러가 감지하기가 어렵습니까? 순수 함수를 호출하는 것이 (어떤 이유로 든) 바람직하지 않다면 대신 컴파일러 진단을 요구하지 않는 이유는 무엇입니까? – UncleBens

+0

비 순수 가상 메소드 호출을 호출하면 현재 유형을 포함하여 대부분 파생 된 구현을 호출하지만 그 유형은 포함하지만 그 자손은 호출하지 않는 효과가 있습니다. 순수 가상 메서드 호출의 효과는 디스패치 메커니즘에 의존 할 수 있으므로 정의되지 않습니다. 대부분의 컴파일러는 합성 코드를 호출하여 현명한 메시지로 응용 프로그램을 진단하고 종료합니다. –

+0

컴파일러가 생성자를 처리하는 시점에서 구현 된 순수 가상 메서드 (정의를 볼 수있는 경우)인지는 알 수 있지만 순수 가상 메서드가 다른 변환 단위로 구현되지 않을 수도 있습니다 (어쩌면). 생성자는 클래스 선언에 인라인되어 있고 순수 가상 메서드가 다른 번역 단위에 정의되어있는 동안 번역 단위에서 포함됩니다) –

관련 문제