C++에서 가상 계승은 무엇입니까? 언제 사용해야 합니까?

우리가 대상을 대상으로 프로그래밍하는 언어로 인코딩을 시작할 때, 우리는 일반적으로 이것이 양호한 계승 차원 구조 구축에 관한 것이라고 생각한다.저희도요.잠시 후, 우리는 우리가 계승보다는 조합을 더 사용해야 한다는 것을 알게 되었다.저희도요.그러나 우리는 여전히 계승이 필요하다. 때로는 문제에 부딪히기도 한다. 이것이 유일한 방법인 것 같다.그때 우리는 좀 더 특수한 유전 형식을 이해할 수 있을 것이다.C++에서는 private inheritance 또는 가상 상속을 의미할 수 있습니다.오늘 우리는 후자에 대해 이야기합시다.

가상 상속은 무엇입니까?


다이아몬드 문제


가상 계승은 C++ 기술로 기본 클래스의 구성원 변수가 2급 파생 클래스(또는 손자 파생 클래스)만 계승할 수 있도록 확보한다.가상 계승이 없는 상황에서 두 개의 클래스 B와 C가 클래스 A를 계승하고 클래스 D가 B와 C를 계승하면 D는 A 구성원 변수의 두 가지 복사본을 포함한다. 하나는 B를 통과하고 하나는 C를 통과한다. 이런 변수는 역할 영역을 통해 독립적으로 접근할 것이다.
반대로 클래스 B와 C가 실제로 클래스 A에서 계승된다면 클래스 D의 대상은 클래스 A에서 온 구성원 변수만 포함할 것이다.
예상한 바와 같이 다중 계승을 처리해야 할 때 이런 기술은 매우 유용할 뿐만 아니라, 악명 높은 마름모꼴 계승을 해결하는 방법이기도 하다.

다중 베이스 인스턴스


실제로 가상 기류에서 파생된 클래스, 특히 가상 기류 자체가 순추상적일 때 가상 기류가 가장 적합하다.이것은 '연결 클래스' (밑에 있는 그) 위의 클래스에 데이터가 거의 없다는 것을 의미한다.
순수한 추상은 아니지만 아래의 유형 차원 구조를 고려하여 마름모꼴 문제를 나타낸다.
struct Person {
    virtual ~Person() = default;
    virtual void speak() {}
};

struct Student: Person {
    virtual void learn() {}
};

struct Worker: Person {
    virtual void work() {}
};

// A teaching assistant is both a worker and a student
struct TeachingAssistant: Student, Worker {};

TeachingAssistant ta;
위에서 말한 바와 같이 aTeachingAssistant.speak()에 대한 호출은 명확하지 않다. Person 중 두 개의 TeachingAssistant(간접) 기류가 있기 때문에 그 어떠한 TeachingAssistant 대상도 두 개의 서로 다른 Person 기류 대상이 있다.Person 대상에 인용된 TeachingAssistant 하위 대상을 직접 연결하는 시도는 실패할 것입니다. 귀속 자체가 명확하지 않기 때문입니다.
TeachingAssistant ta;
Person& a = ta;  // error: which Person subobject should a TeachingAssistant cast into, 
                // a Student::Person or a Worker::Person?
잘못된 뜻을 없애기 위해서 우리는 ta을 두 개의 기류자 대상 중 임의의 하나로 현저하게 전환해야 한다.
TeachingAssistant ta;
Person& student = static_cast<Student&>(ta); 
Person& worker = static_cast<Worker&>(ta);
speak()을 호출하기 위해서는 동일한 변조 또는 명확한 한정이 필요하다. static_cast<Student&>(ta).speak() 또는 static_cast<Worker&>(ta).speak() 또는 ta.Student::speak() 또는 ta.Worker::speak()Person이다.현식한정은 지침과 대상에 대해 더욱 간단하고 통일된 문법을 사용할 뿐만 아니라 정적 분파도 허용하기 때문에 더욱 좋은 방식이라고 할 수 있다.
이 예에서 TeachingAssistant의 이중 계승은 필요 없을 수도 있다. 왜냐하면 우리는 PersonTeachingAssistant의 관계를 한 번만 구축하고 싶기 때문이다.StudentWorker이자 TeachingAssistant이다. 이 사실은 PersonTA의 두 배(Person이 정신분열증을 앓지 않는 경우)를 의미하지 않는다. TeachingAssistant 기류가 TeachingAssistant에 대응하는 계약(위의'예'관계는 실제적으로'실현된 요구'를 의미한다).PersonTeachingAssistant의 계약을 한 번만 집행한다.

한 가지 행위만 있을 거예요.


'한 번만 존재한다'는 현실적 의미는 speak은 두 가지 다른 방법이 아니라 Person::speak()을 실현하는 방법일 뿐이라는 것이다.
우리의 퇴화 사례에서 StudentWorker이나 TeachingAssistant에서 모두 덮이지 않았지만 이것은 다를 수 있다. 그리고 우리는 speak() 방법의 여러 가지 실현을 할 것이다.
만약 우리가 다음과 같은 방식으로 virtual을 우리의 유산에 도입한다면 우리의 문제는 사라질 것이다.
struct Person {
    virtual ~Person() = default;
    virtual void speak() {}
};

// Two classes virtually inheriting Person:
struct Student: virtual Person {
    virtual void learn() {}
};

struct Worker: virtual Person {
    virtual void work() {}
};

// A teaching assistant is still a student and the worker
struct TeachingAssistant: Student, Worker {};
이제 speak()으로 쉽게 전화를 걸 수 있습니다.PersonTeachingAssistant::Worker 부분은 현재 Person이 사용하는 TeachingAssistant::Student의 실례와 같다. 즉, TeachingAssistant은 그 표시에서 공유된 Person의 실례가 하나밖에 없기 때문에 TeachingAssistant::speak에 대한 호출은 명확하다.이 밖에 TeachingAssistant에서 Person으로 직접 전환하는 것도 명확하다. 왜냐하면 Person의 실례만 TeachingAssistant으로 전환할 수 있기 때문이다.
이 작업은 vtable개의 포인터를 통해 수행할 수 있습니다.세부 사항을 깊이 파고들지 않은 상황에서 대상의 크기에 두 개의 지침이 추가되었지만 뒤에는 Person 대상만 있고 다른 뜻은 없었다.
다이아몬드의 중간층에 virtual 키워드를 사용해야 합니다.밑에서 사용하는 것은 도움이 되지 않는다.
자세한 내용은 Core Guidelineshere에서 확인할 수 있습니다.

우리는 가상 계승을 계속 사용해야 합니까?그렇다면, 왜?없으면 왜 없어요?


답안은 정해진 것이 틀림없다. 상투적인 답안의 기초는 C++의 가장 기본적인 이념이다. 당신이 사용하는 물건에 대해서만 비용을 지불해야 한다는 것이다.만약 네가 가상 계승을 필요로 하지 않는다면, 너는 돈을 쓰지 않는 것이 가장 좋다.
가상 상속은 거의 필요 없다.그것은 우리가 본문에서 처음 본 다이아몬드 계승 문제를 해결했다.네가 얼마나 무거운 계승을 해야만 이런 상황이 발생할 수 있다. 그렇지 않으면, 너는 이 문제가 없을 것이다.
동시에 그것도 약간의 결점이 있다.

더욱 복잡한 의존 관계


가상 상속은 대상의 초기화와 복제에 번거로움을 가져올 수 있다.이것은 이러한 조작을 담당하는'최파생'클래스이기 때문에 기본 구조의 모든 세부 사항을 숙지해야 한다.
따라서 클래스 간에 더욱 복잡한 의존 관계가 생겨서 프로젝트 구조를 복잡하게 하고 재구성 기간에 모든 클래스에 대해 추가적인 수정을 할 수 있습니다.이 모든 것은 새로운 오류를 초래하고 코드의 가독성을 떨어뜨리기 때문에 유지보수가 쉽지 않다.

비싼 유형 변환


ISO C++ guidelines은 C 스타일의 하향 변환을 사용하여 기본 바늘을 파생 바늘로 변환할 수 없다고 밝혔다.
이러한 문제는 dynamic_cast을 통해 해결할 수 있지만 성능에 영향을 미칩니다.코드에 너무 많은 dynamic_cast을 사용하면 큰 영향을 미칠 수 있고 프로젝트의 구조가 매우 엉망일 수도 있다는 것을 의미한다.
다중 상속 없이 원하는 기능을 항상 수행할 수 있습니다.이것은 결코 이상하지 않다.가상 계승의 특성은 많은 다른 주요 언어에 존재하지 않지만 대형과 복잡한 프로젝트에 사용된다.

결론


오늘 우리는 다이아몬드 계승 문제를 토론했다.우리는 하나의 기류와 하나의 파생류 사이에 여러 가지 경로가 있을 때 여러 개의 기본 대상이 실례화되는 것은 거의 바람직하지 않다는 것을 안다.C++는 이 문제를 해결하기 위해 가상 계승을 제기했고, 이러한 구조는 기본 클래스의 실례와 공존할 수 있도록 허용했다.
그러나, 당신이 사용하는 것만 지불해야 하기 때문에, 가상 계승은 당신의 기본적인 선택이 되어서는 안 된다.대부분의 프로젝트는 이런 언어 특성이 없는 상황에서 이루어질 수 있다. 만약 당신이 다중 계승이 없는 상황에서 소프트웨어를 설계할 수 있다면, 그 단점을 처리할 필요가 없다.
당신은 생산 코드에서 다중 계승을 사용한 적이 있습니까?만약 그렇다면, 용례는 무엇입니까?

더욱 깊이 연락하다


이 글이 재미있다면 subscribe to my personal blog을 눌러서 계속 연결합시다!

좋은 웹페이지 즐겨찾기