2011-12-20 2 views
5

내가 비슷한 오늘 우연히 캐스팅, 이후 몇 가지를 시도하고 다음 ++ G 법적 것 같다 것으로 나타났습니다 : 그래서C++ 할당

struct A { 
    int val_; 
    A() { } 
    A(int val) : val_(val) { } 
    const A& operator=(int val) { val_ = val; return *this; } 
    int get() { return val_; } 
}; 

struct B : public A { 
    A getA() { return (((A)*this) = 20); } // legal? 
}; 

int main() { 
    A a = 10; 
    B b; 
    A c = b.getA(); 
} 

B::getB 유형 A을 반환, 그 후 값이 20으로 지정되면 (오버로드 된 A::operator=을 통해)

몇 가지 테스트를 거친 후 정확한 값 (c.get은 예상대로 20을 반환합니다)을 반환합니다.

궁금합니다. 은 정의되지 않은 동작입니다.? 이 경우 정확하게 무엇이 그렇게합니까? 그렇지 않다면, 그러한 코드의 장점은 무엇입니까?

+2

'const A & operator = (int val) {val_ = val; }'return 문이 누락 되었습니까? – ruakh

+1

음,'B :: getB'가 없지만 B :: getA'는 A의 인스턴스가 아닌 B의 인스턴스 (A의 하위 클래스)를 반환합니다. 이것은 매우 모호한 코드이지만 표면적으로, 완벽하게 합법적이다. –

+0

@ruakh : 그렇게 보입니다. :) – netcoder

답변

3

:

return (((A)*this) = 20); 

는 ... 속기처럼 (아직 모호한) 구문 : 더 나은

A a(*this); 
return a.operator=(20); 

... 나 :

,
return A(*this) = 20; 

... 따라서 동작이 정의됩니다.

+1

예, 나는'return A (* this) = 20; '라고 써도됩니다. 그것은 전혀 모호하지 않으며, 코드가 얼마나 위험한 지 분명하게 보여줍니다! :-) 모호한 것은 원래 구문입니다! –

+0

@KerrekSB : 100 % 동의합니다. – netcoder

+0

+1. "... 따라서 정의 된 행동입니다." 정의 된 * 반환 유형이 참조가 아닌 경우 * (답장에 이것을 추가 할 필요가 없으며, 요점에 접선이고 나는 내 답을하고 싶지 않습니다!) –

1

여기서 많은 일들이 진행되고 있습니다. 코드는 유효하지만 질문에 잘못된 가정이 있습니다. 당신은

"B :: 게타 반환 [...] 자체에 값 20을 할당, 그 이후로"

이 (내 강조)이 정확하지 않은 말했다. getA는 이 아니며 개체를 수정합니다. 이를 확인하기 위해 메서드 서명에 const을 넣으면됩니다. 나는 완전히 설명 할 것이다.

A getA() const { 
    cout << this << " in getA() now" << endl; 
    return (((A)*this) = 20); 
} 

그럼 어떻게 될까요? 내 sample code 보면 (나는이 답변의 끝에 내 성적 증명서를 복사 한) :

A a = 10; 

이것은 생성자는 A를 선언합니다. 꽤 똑바로. 이 다음 라인은 :

B b; b.val_ = 15; 

B는 생성자가없는, 그래서 (A에서 상속)의 val_ 멤버에 직접 작성해야합니다. 이 superfically이하는 것처럼 보일 수 있지만, 이것은 b을 수정하지 않습니다

b.getA(); 

: 우리는 다음 라인, A c = b.getA();을 고려하기 전에

, 우리는 매우 신중하게 간단한 표현을 고려해야합니다.

결국, 내 샘플 코드는 b.val_을 출력하고 여전히 15와 같습니다. 20으로 변경되지 않았습니다. c.val_은 물론 20으로 변경되었습니다.

getA을보고 (((A)*this) = 20)이 표시됩니다. 이 문제를 해결해 보겠습니다.

this  // a pointer to the the variable 'b' in main(). It's of type B* 
*this // a reference to 'b'. Of type B& 
(A)*this // this copies into a new object of type A. 

여기서 일시 중지 할 수 있습니다. 이 값이 (A&)*this이거나 심지어 *((A*)this)이라면 더 간단한 선이됩니다. 그러나 (A)*this이므로 유형 A의 새 객체를 만들고 관련 슬라이스를 b에서 복사합니다.

(추가 사항 : 슬라이스를 복사하는 방법을 문의하십시오. B& 참조가 있으며 새 A을 만들고 싶습니다.기본적으로 컴파일러는 복사 생성자 A :: A (const A&)을 만듭니다. B& 참조가 자연스럽게 const A&으로 캐스팅 될 수 있기 때문에 컴파일러에서 사용할 수 있습니다.)

특히 this != &((A)*this)입니다. 이것은 당신에게 놀라운 일일 수 있습니다. (추가 : 다른 한편으로 this == &((A&)*this) 보통 (virtual 방법)이 있는지 여부에 따라에) 이제 우리는이 새로운 객체를 가지고

, 우리는이 새로운 값으로 수를두고

((A)*this) = 20 

볼 수 있습니다 . 이 진술은 이 아니며this->val_에 영향을 미칩니다.

getAA&을 반환하도록 변경하면 오류가 발생합니다. 먼저, operator=의 반환 값은 const A&이며 따라서 A&으로 반환 할 수 없습니다. 그러나 반환 유형으로 const A&이 있더라도 getA 내에서 생성 된 임시 로컬 변수에 대한 참조가됩니다. 그러한 것을 반환하는 것은 정의되지 않았습니다.

마지막으로, 우리는 게타 값에 의해 복사 를 반환 현재 코드가 잘 안전하고 왜 c은 게타

A c = b.getA(); 

에서 값 반환이 복사본을 걸릴 것으로 볼 수있다 -한정된.

== 다음 @Kerrek SB와 @Aaron McDaid의 도움으로 면밀하게 조사한 후 전체 프로그램 ==

#include <iostream> 
using namespace std; 
struct A { 
    int val_; 
    A() { } 
    A(int val) : val_(val) { } 
    const A& operator=(int val) { 
     cout << this << " in operator= now" << endl; // prove the operator= happens on a different object (the copy) 
     val_ = val; 
     return *this; 
    } 
    int get() { return val_; } 
}; 

struct B : public A { 
    A getA() const { 
     cout << this << " in getA() now" << endl; // the address of b 
     return (((A)*this) = 20); 
      // The preceding line does four things: 
      // 1. Take the current object, *this 
      // 2. Copy a slice of it into a new temporary object of type A 
      // 3. Assign 20 to this temporary copy 
      // 4. Return this by value 
    } // legal? Yes 
}; 

int main() { 
    A a = 10; 
    B b; b.val_ = 15; 
    A c = b.getA(); 
    cout << b.get() << endl; // expect 15 
    cout << c.get() << endl; // expect 20 
    B* b2 = &b; 
    A a2 = *b2; 
    cout << b2->get() << endl; // expect 15 
    cout << a2.get() << endl; // expect 15 

} 
+0

+1 당신의 노력으로, 나는 당신에게 더 줄 것이지만 나는 할 수 없습니다. 그러나 귀하의 대답은 너무 복잡하기 때문에 자신의 좋은 ... :) – netcoder

+0

또 다른 pedantry 조각은 다음과 같습니다 : getA의 반환 유형을'A &'로 변경하고'operator ='의 반환 유형을' & 또한. 코드가 컴파일되고 실행되지만 위험합니다.이것을 보시려면'= 20'을 삭제하고'return ((A) * this); '을 사용하시면됩니다. "오류 : 형식 'A'" –

+0

@netcoder의 임시 형식에서 유형 'A &'의 비 const 참조를 초기화하지 못했습니다. 동의했다, LOL! –