문제

가상 할당 연산자를 구현하는 동안 나는 재미있는 행동으로 끝났습니다. G ++ 4.1, 4.3 및 vs 2005가 동일한 동작을 공유하기 때문에 컴파일러 글리치가 아닙니다.

기본적으로 가상 연산자 =는 실제로 실행중인 코드와 관련하여 다른 가상 기능과 다르게 행동합니다.

struct Base {
   virtual Base& f( Base const & ) {
      std::cout << "Base::f(Base const &)" << std::endl;
      return *this;
   }
   virtual Base& operator=( Base const & ) {
      std::cout << "Base::operator=(Base const &)" << std::endl;
      return *this;
   }
};
struct Derived : public Base {
   virtual Base& f( Base const & ) {
      std::cout << "Derived::f(Base const &)" << std::endl;
      return *this;
   }
   virtual Base& operator=( Base const & ) {
      std::cout << "Derived::operator=( Base const & )" << std::endl;
      return *this;
   }
};
int main() {
   Derived a, b;

   a.f( b ); // [0] outputs: Derived::f(Base const &) (expected result)
   a = b;    // [1] outputs: Base::operator=(Base const &)

   Base & ba = a;
   Base & bb = b;
   ba = bb;  // [2] outputs: Derived::operator=(Base const &)

   Derived & da = a;
   Derived & db = b;
   da = db;  // [3] outputs: Base::operator=(Base const &)

   ba = da;  // [4] outputs: Derived::operator=(Base const &)
   da = ba;  // [5] outputs: Derived::operator=(Base const &)
}

효과는 가상 연산자 =가 실제 파생 된 객체 ([1]을 통해 호출 될 때 연산자의 기본 버전을 호출하여 [1]과 비교하여 동일한 서명)의 다른 가상 함수와 다른 동작을 가지고 있다는 것입니다. ) 또는 파생 된 참조 ([3]) 기본 참조 ([2])를 통해 호출 될 때 또는 lvalue 또는 rvalue가 기본 참조이고 다른 하나는 파생 된 참조 ([4], [5]).

이 이상한 행동에 대한 현명한 설명이 있습니까?

도움이 되었습니까?

해결책

어떻게 진행되는지는 다음과 같습니다.

내가 [1]로 바꾸면

a = *((Base*)&b);

그런 다음 상황이 당신이 기대하는 방식으로 작동합니다. 자동으로 생성 된 할당 연산자가 있습니다 Derived 다음과 같이 보입니다.

Derived& operator=(Derived const & that) {
    Base::operator=(that);
    // rewrite all Derived members by using their assignment operator, for example
    foo = that.foo;
    bar = that.bar;
    return *this;
}

당신의 예에서 컴파일러는 그것을 추측하기에 충분한 정보가 있습니다 a 그리고 b 유형입니다 Derived 따라서 위의 자동으로 생성 된 연산자를 사용하여 귀하를 호출합니다. 그것이 당신이 얻은 방법입니다 [1]. 내 포인터 캐스팅은 컴파일러가 당신의 방식으로 그렇게하도록 강요합니다. b 유형입니다 Derived 그리고 그것은 사용합니다 Base.

다른 결과는 같은 방식으로 설명 될 수 있습니다.

다른 팁

이 경우 세 가지 연산자가 있습니다.

Base::operator=(Base const&) // virtual
Derived::operator=(Base const&) // virtual
Derived::operator=(Derived const&) // Compiler generated, calls Base::operator=(Base const&) directly

이것은 왜 그것이 base :: operator = (Base const &)처럼 보이는 이유를 설명합니다. [1]. 컴파일러 생성 버전에서 호출됩니다. 케이스에도 동일하게 적용됩니다 [3]. 사례 2에서 오른쪽 인수 'BB'에는 유형 기반이 있으므로 파생 된 :: OPERATOR = (파생)를 호출 할 수 없습니다.

파생 클래스에 정의 된 사용자 제공 할당 연산자는 없습니다. 따라서 컴파일러는 하나의 합성 및 내부적으로 기본 클래스 할당 연산자가 파생 클래스를 위해 합성 할당 연산자로부터 호출됩니다.

virtual Base& operator=( Base const & ) //is not assignment operator for Derived

따라서, a = b; // [1] outputs: Base::operator=(Base const &)

파생 클래스에서 기본 클래스 할당 연산자가 무시되었으므로 재정의 메소드는 파생 클래스의 가상 테이블에서 항목을 가져옵니다. 메소드가 참조 또는 포인터를 통해 호출되면 도출 된 클래스 재정의 메소드는 런타임에 VTable 입력 해상도로 인해 호출됩니다.

ba = bb;  // [2] outputs: Derived::operator=(Base const &)

==> 내부 ==> (Object-> vtable [할당 연산자]) 객체가 속한 클래스의 Vtable에서 할당 연산자의 항목을 가져 와서 메소드를 호출하십시오.

적절한 것을 제공하지 못하는 경우 operator= (즉, 올바른 반환 및 인수 유형), 기본값 operator= 사용자 정의 된 제품을 과부하시키는 컴파일러에서 제공합니다. 귀하의 경우에는 호출 할 것입니다 Base::operator= (Base const& ) 파생 된 멤버를 복사하기 전에.

이것을 확인하십시오 링크 연산자에 대한 자세한 내용은 가상으로 만들어졌습니다.

그 이유는 컴파일러가 기본 할당을 제공 한 이유입니다 operator=. 시나리오에서 호출됩니다 a = b 그리고 우리가 알듯이, 기본값은 내부적으로 기본 할당 연산자를 호출합니다.

가상 할당에 대한 자세한 설명은 다음에서 찾을 수 있습니다. https://stackoverflow.com/a/26906275/3235055

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