문제

나는 "가 무엇인지 알고 싶다.가상 기본 클래스"이며 그 의미는 무엇입니까?

예를 보여드리겠습니다:

class Foo
{
public:
    void DoSomething() { /* ... */ }
};

class Bar : public virtual Foo
{
public:
    void DoSpecific() { /* ... */ }
};
도움이 되었습니까?

해결책

가상 상속에 사용되는 가상 기본 클래스는 다중 상속을 사용할 때 특정 클래스의 여러 "인스턴스"가 상속 계층 구조에 나타나는 것을 방지하는 방법입니다.

다음 시나리오를 고려해보세요.

class A { public: void Foo() {} };
class B : public A {};
class C : public A {};
class D : public B, public C {};

위의 클래스 계층 구조는 다음과 같은 "두려운 다이아몬드"를 생성합니다.

  A
 / \
B   C
 \ /
  D

D의 인스턴스는 A를 포함하는 B와 A도 포함하는 C로 구성됩니다.따라서 A의 두 "인스턴스"(더 나은 표현을 원함)가 있습니다.

이 시나리오에서는 모호할 가능성이 있습니다.이 작업을 수행하면 어떻게 되나요?

D d;
d.Foo(); // is this B's Foo() or C's Foo() ??

가상 상속은 이 문제를 해결하기 위해 존재합니다.클래스를 상속할 때 virtual을 지정하면 단일 인스턴스만 원한다고 컴파일러에 알리는 것입니다.

class A { public: void Foo() {} };
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};

이는 계층 구조에 A의 "인스턴스"가 하나만 포함되어 있음을 의미합니다.따라서

D d;
d.Foo(); // no longer ambiguous

간략한 요약으로 도움이 되기를 바랍니다.자세한 내용은 다음을 읽어보세요. 이것 그리고 이것.좋은 예도 있습니다 여기.

다른 팁

메모리 레이아웃 정보

참고로 Dreaded Diamond의 문제점은 기본 클래스가 여러 번 존재한다는 것입니다.따라서 일반 상속을 사용하면 다음과 같은 이점이 있다고 생각합니다.

  A
 / \
B   C
 \ /
  D

그러나 메모리 레이아웃에는 다음이 있습니다.

A   A
|   |
B   C
 \ /
  D

전화할 때 이유를 설명합니다. D::foo(), 모호성 문제가 있습니다.하지만 진짜 문제는 멤버 변수를 사용하려고 할 때 발생합니다. A.예를 들어 다음과 같은 경우가 있다고 가정해 보겠습니다.

class A
{
    public :
       foo() ;
       int m_iValue ;
} ;

액세스하려고 할 때 m_iValue ~에서 D, 컴파일러는 항의할 것입니다. 계층 구조에서 두 개가 표시되기 때문입니다. m_iValue, 하나도 아닙니다.그리고 하나를 수정하면 다음과 같이 말해보세요. B::m_iValue (그건 A::m_iValue ~의 부모 B), C::m_iValue 수정되지 않습니다(즉, A::m_iValue ~의 부모 C).

가상 상속이 유용한 곳은 바로 여기입니다. 이를 통해 진정한 다이아몬드 레이아웃으로 돌아갈 수 있습니다. foo() 방법뿐 아니라 단 하나의 방법이기도 합니다. m_iValue.

무엇이 잘못될 수 있나요?

상상하다:

  • A 몇 가지 기본 기능이 있습니다.
  • B 여기에 일종의 멋진 데이터 배열을 추가합니다(예:)
  • C 여기에 관찰자 패턴과 같은 멋진 기능을 추가합니다(예: m_iValue).
  • D ~로부터 상속받다 B 그리고 C, 따라서 A.

일반 상속을 사용하면 수정 m_iValue ~에서 D 모호하며 이 문제를 해결해야 합니다.그렇다 해도 2가지가 있다. m_iValues 내부에 D, 따라서 이를 기억하고 동시에 두 가지를 업데이트하는 것이 좋습니다.

가상 상속을 사용하여 수정 m_iValue ~에서 D 괜찮아...하지만...당신이 가지고 있다고 가정 해 봅시다 D.이를 통해 C 인터페이스, 관찰자를 연결했습니다.그리고 그것을 통해 B 인터페이스에서는 직접 변경하는 부작용이 있는 멋진 배열을 업데이트합니다. m_iValue...

변경됨에 따라 m_iValue (가상 접근자 메서드를 사용하지 않고) 직접 수행되면 관찰자는 이를 통해 "듣기" C 청취를 구현하는 코드가 다음과 같기 때문에 호출되지 않습니다. C, 그리고 B 그것에 대해 모른다 ...

결론

계층 구조에 다이아몬드가 있다면 해당 계층 구조에 문제가 있을 가능성이 95%라는 의미입니다.

가상 베이스를 사용한 다중 상속을 설명하려면 C++ 개체 모델에 대한 지식이 필요합니다.주제를 명확하게 설명하는 것은 댓글 상자가 아닌 기사에서 수행하는 것이 가장 좋습니다.

이 주제에 대한 모든 의심을 해결한 가장 읽기 쉬운 설명은 다음 기사였습니다. http://www.phpcompiler.org/articles/virtualinheritance.html

해당 내용을 읽은 후에는 해당 주제에 대해 다른 내용을 읽을 필요가 없습니다(컴파일러 작성자가 아닌 이상).

가상 기본 클래스는 인스턴스화 할 수없는 클래스입니다.직접 객체를 만들 수 없습니다.

나는 당신이 매우 다른 두 가지를 혼동하고 있다고 생각합니다.가상 상속은 추상 클래스와 다릅니다.가상 상속은 함수 호출의 동작을 수정합니다.때로는 모호한 함수 호출을 해결하고, 때로는 비가상 상속에서 예상되는 클래스가 아닌 다른 클래스에 대한 함수 호출 처리를 연기합니다.

OJ의 친절한 설명에 추가하고 싶습니다.

가상 상속에는 대가가 따르지 않습니다.가상의 모든 것과 마찬가지로 성능 저하가 발생합니다.이 성능 저하를 덜 우아하게 해결할 수 있는 방법이 있습니다.

가상으로 파생하여 다이아몬드를 깨는 대신 다이아몬드에 다른 레이어를 추가하여 다음과 같은 결과를 얻을 수 있습니다.

   B
  / \
D11 D12
 |   |
D21 D22
 \   /
  DD

가상으로 상속되는 클래스는 없으며 모두 공개적으로 상속됩니다.클래스 D21 및 D22는 아마도 함수를 private으로 선언하여 DD에 대해 모호한 가상 함수 f()를 숨길 것입니다.이들은 각각 래퍼 함수 f1()과 f2()를 정의하고 각각 클래스 로컬(비공개) f()를 호출하여 충돌을 해결합니다.클래스 DD는 D11::f()를 원하면 f1()을 호출하고 D12::f()를 원하면 f2()를 호출합니다.래퍼를 인라인으로 정의하면 오버헤드가 거의 없을 것입니다.

물론 D11과 D12를 변경할 수 있다면 이러한 클래스 내에서 동일한 작업을 수행할 수 있지만 그렇지 않은 경우가 많습니다.

다중 상속과 가상 상속에 대해 이미 언급한 내용 외에도 Dr Dobb의 저널에 매우 흥미로운 기사가 ​​있습니다. 유용한 것으로 간주되는 다중 상속

좀 혼란스러워지네요.당신이 몇 가지 개념을 혼동하고 있는지 모르겠습니다.

OP에 가상 기본 클래스가 없습니다.기본 클래스가 있습니다.

가상 상속을 하셨습니다.이는 일반적으로 여러 파생 클래스가 기본 클래스의 멤버를 재생산하지 않고 사용하도록 다중 상속에 사용됩니다.

순수 가상 함수가 있는 기본 클래스는 인스턴스화되지 않습니다.이를 위해서는 Paul이 사용하는 구문이 필요합니다.일반적으로 파생 클래스가 해당 함수를 정의해야 하는 데 사용됩니다.

나는 당신이 묻는 것을 완전히 이해할 수 없기 때문에 이것에 대해 더 이상 설명하고 싶지 않습니다.

이는 가상 함수에 대한 호출이 "올바른" 클래스로 전달된다는 의미입니다.

C++ FAQ 라이트 FTW.

즉, "다이아몬드" 계층 구조가 형성되는 다중 상속 시나리오에서 자주 사용됩니다.그런 다음 가상 상속은 해당 클래스에서 함수를 호출하고 해당 함수를 해당 하위 클래스 위의 클래스 D1 또는 D2로 확인해야 할 때 하위 클래스에 생성된 모호성을 깨뜨립니다.참조 FAQ 항목 다이어그램과 세부정보를 확인하세요.

그것은 또한에서 사용됩니다 자매 대표단, 강력한 기능입니다(심약한 사람에게는 해당되지 않음).보다 이것 자주하는 질문.

Effective C++ 3판의 항목 40(2판의 43)도 참조하세요.

다이아몬드 상속 실행 가능 사용 예

이 예에서는 일반적인 시나리오에서 가상 기본 클래스를 사용하는 방법을 보여줍니다.다이아몬드 상속을 해결합니다.

#include <cassert>

class A {
    public:
        A(){}
        A(int i) : i(i) {}
        int i;
        virtual int f() = 0;
        virtual int g() = 0;
        virtual int h() = 0;
};

class B : public virtual A {
    public:
        B(int j) : j(j) {}
        int j;
        virtual int f() { return this->i + this->j; }
};

class C : public virtual A {
    public:
        C(int k) : k(k) {}
        int k;
        virtual int g() { return this->i + this->k; }
};

class D : public B, public C {
    public:
        D(int i, int j, int k) : A(i), B(j), C(k) {}
        virtual int h() { return this->i + this->j + this->k; }
};

int main() {
    D d = D(1, 2, 4);
    assert(d.f() == 3);
    assert(d.g() == 5);
    assert(d.h() == 7);
}

가상 수업은 ~ 아니다 가상 상속과 동일합니다.인스턴스화할 수 없는 가상 클래스, 가상 상속은 완전히 다른 것입니다.

Wikipedia는 나보다 더 잘 설명합니다. http://en.wikipedia.org/wiki/Virtual_inheritance

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top