2011-07-01 2 views
38

이것은 인터뷰 질문이었습니다. a = b; 완벽하게 잘하는 동안,기본 클래스에 대한 할당이 유효하지만 파생 클래스에 대한 컴파일 오류가 할당되는 이유는 무엇입니까?

struct A {}; 
struct B : A {}; 
A a; 
B b; 
a = b; 
b = a; 

b = a;이 오류가 발생 않습니다 다음 고려? 당신의 예에서

struct A {int someInt;}; 
struct B : A {int anotherInt}; 
A a; 
B b; 

/* Compiler thinks: B inherits from A, so I'm going to create 
    a new A from b, stripping B-specific fields. Then, I assign it to a. 
    Let's do this! 
*/ 
a = b; 

/* Compiler thinks: I'm missing some information here! If I create a new B 
    from a, what do I put in b.anotherInt? 
    Let's not do this! 
*/ 
b = a; 

:

+1

무엇이 오류입니까? – MGZero

+3

사실 그건 아주 좋은 질문입니다. 첫 번째 진술이 효과가있는 이유는 즉시 명백하지 않습니다. –

+0

구조체가 클래스처럼 동작한다고 가정합니다.이 구조체는 모든 고유 한 'B'구조체 항목을 잘라 내고 모든 'a'를 복사합니다. – DanTheMan

답변

62

암시 적으로 선언 된 복사 할당 연산자 B은 암시 적으로 선언 된 복사 할당 연산자 A을 숨 깁니다.

라인 b = a의 경우 operator=B 만 후보입니다. 그러나 해당 매개 변수의 유형은 이며 A 인수로 초기화 할 수 없습니다 (다운 캐스트가 필요함). 그래서 당신은 오류가 발생합니다.

+3

정답입니다. – Puppy

+14

'B'의 정의를'struct B : A {A :: operator =; }; 두 줄이 모두 컴파일된다는 것을 관찰하십시오. –

+0

총체적으로 좋은 대답입니다. 다른 답변은 철학적 인 반면,이 답변은 작동하지 않는 특정 기술적 인 이유가 포함되어 있습니다. –

24

모든 B가하는 A이기 때문에,하지만 모든 A가 B를

편집은 상황이 조금 더 명확하게 의견을 다음과 같다 (나는 당신의 변형 예) 속성이 someInt도 아니고 anotherInt도 없으므로 일 수 있습니다. 그러나 컴파일러는 어쨌든 그것을 허용하지 않을 것입니다.

+8

-1 : 철학적으로 대답은 정확하지만 철학은 포인터를 사용하여 객체를 참조 할 때 코드로 직접 변환됩니다. 암시적인'A :: operator ='와'B :: operator ='missing에 대한 기술적 인 세부 사항이 있습니다. –

+1

어리석은 downvotes 카운터 +1 그들은 아무리 설명해도 –

+0

@ LokiAstari : 틀렸어. 대답은 정확합니다. C++ 기술은별로 없습니다. 다른 언어는 약간 다른 방식으로 동일합니다. 세부 사항을 암기하고 "왜"를 이해하지 못하는 것보다 "왜"가 아닌 기술적 인 C++ 수준의 세부 사항을 이해하는 것이 낫습니다. litb의 대답은 "이유"부서에서 조금 부족합니다. –

4

나는 명백한 이유를 확인하기 위해 구조체의 이름을 변경했습니다 : 분명히

struct Animal {}; 
struct Bear : Animal {}; 
Animal a; 
Bear b; 
a = b; // line 1 
b = a; // line 2 

는, 어떤 곰은 또한 동물입니다 만, 모든 동물은 곰을 간주 될 수 없습니다.

모든 B "isa"A의 모든 인스턴스는 A의 인스턴스 여야합니다. 정의에 따라 A의 다른 인스턴스와 동일한 순서로 같은 멤버를가집니다. b를 a로 복사하면 B가 손실됩니다. 특정 멤버를 완전히 채우지 만 A의 요구 사항을 충족하는 구조체를 완전히 채 웁니다. 반면에 a를 b로 복사하면 B가 A보다 많은 멤버를 가질 수 있기 때문에 b가 불완전하게 남을 수 있습니다. A 나 B 모두 전혀 멤버가 없지만 컴파일러가 한 할당 만 허용하고 다른 할당은 허용하지 않는 이유입니다.

+2

이것은 Cicada의 답변과 같은 이유로 잘못되었습니다. 설명을 보려면 여기에 주석을보십시오. 아, 그리고 다른 이유로는 잘못되었습니다 (LSP). –

+2

이름을 변경해도 더 이상 나에게 분명하지 않습니다. –

+0

사각형/사각형 상속은 Rect가 Rect의 서브 클래스가되어야한다고 주장 할 수있는 것처럼 "is-a"라는 상속이 반 직관적 인 고전적인 예입니다. 더 좋은 예를들 수 있습니까? –

1

내가 인터뷰하는 경우, 나는 거의 철학적 방법으로 설명하지 않을 것입니다. 모든 B 그 일환으로 A 포함되어 있기 때문에

a = b; 

는 유효합니다. 따라서 aB에서 A을 추출 할 수 있습니다. 그러나 A에는 B이 포함되어 있지 않습니다. 따라서 bA에서 B을 찾을 수 없습니다. 그 이유는

b = a; 

은 (는) 잘못된 것입니다.

[유비하는 void* 어떤 Type*에서 발견 될 수 있지만, Type*는 (따라서 우리는 캐스트 필요) void*에서 찾을 수 없습니다.]

+0

이것은 ** 잘못된 ** 것입니다. 첫 번째 변환은 손실입니다 (즉, 정보 손실이 발생합니다). 그렇다면 왜 유효합니까? 그것은해서는 안됩니다. 'void *'로의 변환이 손실이 없기 때문에 유추에 결함이 있습니다. –

+4

@Konrad, 나는 동의하지 않는다. 정보는 손실되지만 b 객체의 B 특정 부분은 객체가 유효한 A가 될 필요가 없으므로 B 특정 구성원을 분리 할 수 ​​있습니다.이것은 비 가상 함수에 문제를 야기하기 때문에 C++ 표준의 토론 된 부분입니다. 일반 데이터 개체의 경우에는 문제가 없어야합니다. –

+1

@Martin 심지어 평범한 데이터 객체도이 손실로 고통을 겪습니다. 'double'에서'int'로 (허용 된) 암시 적 변환을 고려하십시오. C++에서 허용하는 것은 사실입니다. 현대의 대부분의 언어는 위험하기 때문에 이것을 금지합니다. –

3

기억 명시 적으로 선언 복사 할당 연산자 하나 개의 뜻이 아니라면 그 암시 적으로 선언되고 모든 클래스에 대해 정의됩니다 (및 구조체는 C++의 클래스입니다).

struct A를 들어 다음과 같은 서명을해야합니다 :

A& A::operator=(const A&) 

을 그리고 그것은 단순히 하위 객체의 memberwise 할당을 수행한다. BA::operator=(const A&)에 대한 const A& 매개 변수와 일치하기 때문에

a = b;은 OK입니다. A의 구성원 만 대상에 '회원 순 지정'되어 있기 때문에 A의 일부가 아닌 B의 구성원이 손실됩니다.이를 '조각'이라고합니다.

struct B 위해 implcit 할당 연산자는 다음과 같은 서명을해야합니다 : Aconst B& 인수와 일치하지 않기 때문에

B& B::operator=(const B&) 

b = a; 확인하지 않습니다.

6

BA입니다하지만 AB 아니라, 당신이 A 's 및 B 년대 포인터 또는 참조로 작업 할 때이 사실은 직접 적용될 수 있음이 사실이다. 문제는 할당 연산자입니다. 아래 할당 할 때

struct A {}; 
struct B : A {}; 

그래서

struct A { 
    A& operator=(const A&); 
}; 
struct B : A { 
    B& operator=(const B&); 
}; 

하는 것과 같습니다

A a; 
B b; 
a = b; 

B이기 때문에 a에 할당 연산자가 b의 인수로 호출 할 수 있습니다 A이므로 bA&으로 대입 연산자에 전달할 수 있습니다. a의 대입 연산자는 A에있는 데이터에 대해서만 알고 있으며 B에있는 데이터는 알지 못하므로 A의 일부가 아닌 B의 구성원은 모두 손실됩니다.이를 '조각'이라고합니다.

하지만 할당하려고 할 때 :

b = a; 

a 그렇게 ab의 할당 연산자에 B& 매개 변수와 일치 할 수 없습니다하는 B없는 유형 A,이다.

b=a은 상속 된 A& A::operator=(const A&)을 호출해야한다고 생각 하겠지만, 그렇지 않습니다. 할당 연산자 B& B::operator=(const B&)A에서 상속 될 연산자를 숨 깁니다. using A::operator=; 선언을 사용하여 다시 복원 할 수 있습니다.

관련 문제