2010-03-15 11 views
14

이해했듯이 operator =을 (를) 오버로드하면 반환 값은 비 const 참조 여야합니다.C++에서 오버로드 할당 연산자


A& A::operator=(const A&) 
{ 
    // check for self-assignment, do assignment 

    return *this; 
} 

그것은 비 const 멤버 함수가 같은 경우에 호출 할 수 있도록 const가 아닌 것입니다 :


(a = b).f(); 

그러나 왜 참조를 반환해야? 반환 값이 참조로 선언되지 않은 경우 어떤 인스턴스에서 문제가 발생합니까? 값으로 반환한다고 가정 해 봅시다.

복사 생성자가 올바르게 구현되었다고 가정합니다.

+1

사람들이 표현식이 아닌 성명서를 다루기를 원하면 'void'를 반환 할 수 있습니다. 그것은'(a = b) = c','a = (b = c)'와 값과 참조의 차이를 드러내는 다른 속임수를 멈출 것이다. –

+0

오브젝트가 스택에서 벗어 났을 때 오브젝트가 자동으로 삭제되는 것을 막아야 할 때 할당 연산자에서 void를 리턴하는 것이 유용하다는 것을 알게되었습니다. 참조 카운트 된 객체의 경우 소멸자가 모르는 사이에 소멸자가 호출되는 것을 원하지 않습니다. – cjcurrie

답변

15

참조를 반환하지 않으면 리소스가 낭비되고 이상한 디자인이됩니다. 거의 모든 사용자가 해당 값을 삭제하더라도 운영자의 모든 사용자에 대해 사본을 작성하려는 이유는 무엇입니까? 운영자입니다 오버로드 할 때 내장 된 할당 연산자 마찬가지로

int &a = (some_int = 0); // works 
+0

예, 이해합니다. 낭비입니다. 값 또는 참조에 의한 반환이 할당 연산을 잘못되었거나 잘못된 값으로 만드는 경우가 있는지 궁금합니다. – jasonline

+0

@Johannes : 죄송합니다. 전 마지막 문장을받지 못했습니다. 설명해 주겠다고? – jasonline

+0

@jasonline : obj1 = obj2는 임시 값을 반환합니다. 누군가가 클래스를 사용하여 (obj1 = obj2)에 대한 참조를 만들려고 할 때 다음을 볼 수 있습니다. 1 - const가 아닌 참조 인 경우 컴파일하지 않습니다. 2 - 임시 객체에 대한 참조를 만듭니다 (obj1이 아니라). 또는 obj2) 원시 타입이 그런 식으로 작동하지 않기 때문에 혼란스럽게 할 것입니다 (litb의 예를보세요). – tiftik

3

얼마나 자주하고 싶은지 잘 모르겠지만 다음과 같이 입력하십시오. (a=b)=c;에 대한 참조가 필요합니다.

편집 : 좋아요, 그보다 약간 더 있습니다. 많은 추론은 반 역사적이다. 임시 객체에 불필요한 복사본을 피하는 것보다 rvalue를 반환하지 않으려는 이유가 더 있습니다. 원래 앤드류 코닉 게시 한 예에 (작은) 변화를 사용하여,이 같은 고려 : 지금

struct Foo { 
    Foo const &assign(Foo const &other) { 
     return (*this = other); 
    } 
}; 

당신이 C의 이전 버전을 사용 ++ 곳에 할당이를 rvalue를 반환한다고 가정. 이 경우 (*this=other);은 일시적으로 반환됩니다. 그런 다음 임시에 대한 참조를 바인딩하여 임시를 삭제하고 마지막으로 파괴 된 임시 참조를 반환합니다.

참조를 초기화하는 데 사용 된 임시 수명을 연장 한 이후로 규정 된 규칙은이 문제를 최소한 완화하고 완전히 치료할 수 있지만 이러한 규칙이 적용된 후에는 누구도이 특정 상황을 다시 방문하지 않을 것입니다. 작성되었습니다. 그것은 하드웨어의 다른 버전과 변형에서 수십 개의 버그를 해결하기위한 kludges를 포함하는 추악한 장치 드라이버와 비슷합니다. 아마도 리팩토링되고 단순화 될 수 있지만, 겉으로보기에는 무해한 변경으로 인해 현재 작동하며, 궁극적으로 아무도 도움을 줄 수있는 사람이 없다고 생각합니다.

+0

예, 필요합니다. 하지만 당신 말이 맞아요, 그런 식으로 코딩하는 것은 이상합니다. – jasonline

+1

(a = b) = c라고 써야합니다. –

+0

그런 "끔찍한"일을하는 "C +"프로그래머가 많이 있습니다. 나는 이런 저런 공포를 자주 보아 저의 저예산 슬래셔 영화에서 살고있는 것 같은 기분입니다. –

14

좋은 일반적인 조언을 복사하지 않기 때문에 또한

a = b; // huh, why does this create an unnecessary copy? 

, 그것은, 클래스의 사용자들에게 놀라운 것 '으로 할 기본 유형 do '이며 기본 유형에 대한 할당의 기본 동작은 that입니다.

필요성을 느끼는 경우 다른 표현식 내부에서 할당을 사용하지 않도록 설정하는 옵션이있을 수 있지만 사본을 반환하는 것은 전혀 이해가되지 않습니다. 호출자가 복사본을 만들고 싶다면 복사본을 만들 수 있습니다. 참고로 사본이 필요하지 않은 경우 필요하지 않은 임시 정보를 생성 할 필요가 없습니다.

4

원인 f()으로 수정할 수 있습니다.우리가 의 (사본) 값을 반환하는 경우

을 (우리가 const가 아닌 참조를 반환)을, F()이 복사본을 수정하지 않습니다 실제 코드에서는

+0

네, 나도 동의합니다. – jasonline

+0

왜 const 참조를 반환하지 않습니까? 이렇게하면 복사본을 만들지 않고 반환 된 객체를 수정할 수 없습니다. –

1

(즉, (a=b)=c과 같지 않음) 값을 반환하면 컴파일 오류가 발생할 가능성은 낮지 만 사본을 만드는 것이 종종 비쌀 수 있으므로 사본을 반환하는 것은 비효율적입니다.

참조가 필요한 상황을 분명히 생각해 낼 수는 있지만 실제로는 거의 일어나지 않습니다.

+0

예, 비쌉니다. 너의 의도를 알 겠어. – jasonline

0

복사본을 반환 한 경우 거의 모든 중요하지 않은 개체에 대해 복사본 생성자를 구현해야합니다.

복사 생성자를 private로 선언하지만 할당 연산자를 public으로 남겨두면 문제가 발생합니다 ... 할당 연산자를 클래스 나 인스턴스 외부에서 사용하려고하면 컴파일 오류가 발생합니다.

앞서 언급 한 더 심각한 문제는 말할 것도 없습니다. 객체의 복사본이되는 것을 원하지는 않습니다. 실제로 동일한 객체를 참조하기를 원합니다. 하나에 대한 변경 사항은 두 가지 모두에 표시되어야하며 사본을 반환하면 작동하지 않습니다.

2

과제 연산자는 const를 참조 매개 변수를 사용하지 않는 경우 :

A& A::operator=(A&); // unusual, but std::auto_ptr does this for example. 

또는 클래스 A이 (? 참조 계산) 가변 구성원이있는 경우, 다음은 할당 연산자가 할당되는 오브젝트를 변경하는 것이 가능하다

에서 할당 된뿐만 아니라. 이 같은 코드를 가지고 그런 경우 : b = c 할당이 먼저 발생하고, 사본을 반환

a = b = c; 

b에 대한 참조를 반환하는 대신 값 (IT b' 전화). a = b' 지정이 완료되면 변경 할당 연산자는 b 대신 b' 복사본을 변경합니다.

다른 잠재적 인 문제 - 가상 할당 연산자가있는 경우 참조 대신 값으로 반환하면 조각이 발생할 수 있습니다. 그게 좋은 생각이라고 말하는 것은 아니지만 문제가 될 수 있습니다.

(a = b).f()과 같은 작업을 수행하려는 경우 f()이 개체를 변형 시키면 임시로 변경되지 않도록 참조로 반환 할 수 있습니다.

1

잘못된 것을 돌려 보내면 의도하지 않은 부작용이 발생한다고 걱정되면 operator=()을 작성하여 void으로 보낼 수 있습니다. 나는 이것을 수행하는 코드를 보아왔다. (나는 게으름을 추측하거나 리턴 타입이 '안전'보다 무엇인지 알지 못한다.) 그것은 거의 문제를 일으키지 않는다. 일반적으로 operator=()이 반환하는 참조를 사용해야하는 표현식은 거의 사용되지 않으며 거의 ​​항상 쉬운 코드가 대신 사용됩니다.

void을 돌려 줄 것이라고 확신하지는 않습니다 (코드 검토에서 내가하지 말아야 할 일이라고 생각합니다).하지만 고려해야 할 옵션으로 던지고 있습니다. 당신은 할당 연산자의 이상한 사용이 어떻게 처리 될지 걱정할 필요가 없다.


후반 편집 : 또한

, 내가 원래 당신이 당신을함으로써 그 차이를 분할 할 수 있음을 언급해야한다는 operator=() 돌려 const& - 아직 할당 체인 허용됩니다

a = b = c; 

그러나 좀 더 특이한 용도를 허용하지 않습니다.

(a = b) = c; 

이것은 대입 연산자가 C에있는 것과 유사한 의미를 가지며, 연산자 =에 의해 반환 된 값은 lvalue가 아닙니다. C++에서 표준이 바뀌어 = 연산자가 왼쪽 피연산자 형식을 반환하므로 lvalue가되지만 스티브 제시록 (Steve Jessop)이 다른 답변에 주석에서 언급했듯이 컴파일러가 받아 들일 수 있도록 컴파일러가 받아들이도록했습니다.

(a = b) = c; 

심지어 내장형 인 경우 a이 중간 점 시퀀스 포인트없이 두 번 수정되므로 내장형에 대한 정의되지 않은 동작이 발생합니다. 이 문제는 operator=() 함수 호출이 시퀀스 포인트이기 때문에 operator=()이있는 내장되지 않은 호출에서는 피할 수 있습니다.

+0

@Michael : C와 C++의 차이점과 시퀀스 포인트에 대한 추가 설명을 해주셔서 감사합니다. 나는 차이가 있다고 생각한 적이 없었다. 어쨌든, 저는 어떻게 그것을 올바른 방법으로 구현할 것인가 (원시적 인 방법처럼)와 왜 그렇게 구현하는지에 대해서만 관심이 있습니다. 나는 chaining을 불가능하게 할 것이기 때문에 그것이 무효로 돌아가는 것을 의도하지는 않는다. 그것은 일반적으로 허용되어야한다. – jasonline

1

이것은 Scott Meyers의 우수한 책의 항목 10, Effective C++입니다. operator=에서 참조를 반환하는 것은 규칙 일 뿐이지 만 좋은 방법입니다.

이것은 관습에 불과합니다. 코드를 따르지 않는 코드는 컴파일됩니다. 그러나 규약 뒤에 표준 라이브러리의 모든 유형뿐만 아니라 모든 내장 유형이 따라옵니다. 다르게 일하는 좋은 이유가 없다면하지 마십시오.

+0

예, 이것에 대해 읽었습니다. 실제로 잘못된 값을 초래할 수있는 몇 가지 인스턴스를 찾고 있었지만 대부분의 대답은 효율성 문제라고 생각합니다. – jasonline

0

참조로 돌아 오면 연결 작업을 수행하는 시간이 줄어 듭니다. E. g. :

a = b = c = d; 

값에 의해 operator= 반환하는 경우의이 호출됩니다 작업을 보자.

  1. 복사 할당 opertor는 = c에 대한 d 동일 c을 만든 다음 익명 객체 (호출의 ctor 복사) 일시적으로 만듭니다. tc이라고합시다.
  2. 그러면 operator = for b이 호출됩니다. 오른쪽 개체는 tc입니다. 이동 대입 연산자가 호출됩니다. btc과 동일 해집니다. 그런 다음 b 사본을 임시 익명으로 사용하려면 tb으로 전화하십시오.
  3. 동일한 문제가 다시 발생하면 a.operator=a의 임시 복사본을 반환합니다.운영자 ; 후 세 임시 개체는

전부 파괴 : 3 복사 ctors, 2 개 이동 통신 사업자,

이의이 경우 운영자가 변경됩니다 것을 보자 1 부 연산자 = 참조 값을 반환합니다 :

  1. 복사 할당 연산자가 호출되었습니다. cd과 같아지며, 왼쪽 값 객체에 대한 참조가 반환됩니다.
  2. 동일합니다. bc과 같아지며, lvalue 객체에 대한 참조가 반환됩니다.
  3. 동일합니다. ab 동일하게, 오브젝트를 좌변하는 기준은 전부

을 반환하지 않습니다 : 세 개의 사본 사업자라고하며 전혀 ctors!

const 참조로 값을 반환하는 것이 좋습니다. 까다롭고 눈에 잘 띄지 않는 코드를 작성할 수 없습니다. 더 깨끗한 코드를 발견하면 버그가 훨씬 쉽게 될 것입니다. (a = b).f();은 두 줄로 나누는 것이 더 좋습니다 a=b; a.f();.

P. : 복사 할당 연산자 : operator=(const Class& rhs).

이동 할당 연산자 : operator=(Class&& rhs).