연산자 오버로딩 : 멤버 함수 대 비 멤버 함수?


121

멤버 함수로 선언 된 오버로드 된 연산자는 하나의 매개 변수 만 가질 수 있고 자동으로 전달되는 다른 매개 변수는 포인터 이기 때문에 비대칭 이라는 것을 읽었습니다 this. 따라서 이들을 비교할 기준이 없습니다. 반면 a friend로 선언 된 오버로드 된 연산자는 동일한 유형의 두 인수를 전달하므로 비교할 수 있기 때문에 대칭 입니다.

내 질문은 포인터의 lvalue를 참조와 비교할 수 있는데 왜 친구가 선호됩니까? (비대칭 버전을 사용하면 대칭과 동일한 결과가 나타납니다.) STL 알고리즘이 대칭 버전 만 사용하는 이유는 무엇입니까?


11
귀하의 질문은 실제로 이항 연산자에 관한 것입니다. 오버로드 된 모든 연산자가 단일 매개 변수로 제한되는 것은 아닙니다. () 연산자는 여러 매개 변수를 사용할 수 있습니다. 반면 단항 연산자는 매개 변수를 가질 수 없습니다.
— Charles Salvia 2011 년


4
이것은에서 다루는 많은 주제 중 하나입니다 C ++ FAQ : 연산자 오버로딩
— 벤 보이트

답변:


148

연산자 오버로드 된 함수를 멤버 함수로 정의하면 컴파일러는 식 s1 + s2을 s1.operator+(s2). 즉, 연산자 오버로드 된 멤버 함수가 첫 번째 피연산자에서 호출됩니다. 이것이 멤버 함수가 작동하는 방식입니다!

그러나 첫 번째 피연산자가 클래스가 아니면 어떻게 될까요? 첫 번째 피연산자가 클래스 유형이 아닌 연산자를 오버로드하려는 경우 큰 문제가 double있습니다. 그래서 당신은 이렇게 쓸 수 없습니다 10.0 + s2. 그러나 같은 식에 대해 연산자 오버로드 멤버 함수를 작성할 수 있습니다 s1 + 10.0.

이 순서 문제 를 해결하기 위해 멤버 friend에 액세스해야하는 경우 연산자 오버로드 함수를 정의 private합니다. 비공개 회원에 액세스해야 할 friend때만 만드십시오 . 그렇지 않으면 단순히 캡슐화 를 향상시키기 위해 친구가 아닌 비회원 함수로 만드십시오 !

class Sample
{
 public:
    Sample operator + (const Sample& op2); //works with s1 + s2
    Sample operator + (double op2); //works with s1 + 10.0

   //Make it `friend` only when it needs to access private members. 
   //Otherwise simply make it **non-friend non-member** function.
    friend Sample operator + (double op1, const Sample& op2); //works with 10.0 + s2
}

읽어보기 :
피연산자 순서의 약간의 문제
비 멤버 함수가 캡슐화를 개선하는 방법


2
"그것을 확인 friend이 액세스 개인 members..and에 필요로하는 경우에만 당신이 해달라고하면 / 우, 쓰기 접근 지루하다?
— badmaash

4
@Abhi : 선택 선택 : 향상된 캡슐화 vs 게으른 쓰기 습관!
— Nawaz 2011 년

6
@matthias, 모든 연산자가 교환 가능한 것은 아닙니다. 간단한 예는 a/b.
— EDA-QA-모트 ORA-Y

3
비 멤버 운영자가 요구하는 것을 피하는 일반적인 방법 friend은 오퍼레이션 할당 오퍼레이터 (거의 확실하게 공개 멤버가 될 것임) 측면에서 구현하는 것입니다. 예를 들어 T T::operator+=(const T &rhs)멤버로 정의한 다음 비 멤버 T operator(T lhs, const T &rhs)를 return lhs += rhs;. 비 멤버 함수는 클래스와 동일한 네임 스페이스에 정의되어야합니다.
— Adrian McCarthy

2
@ricky :하지만 lhs가 복사본이면 (내 의견에있는 것처럼) lhs가 변경된다는 사실은 중요하지 않습니다.
— Adrian McCarthy

20

전역 연산자 오버로드와 멤버 함수 연산자 오버로드 friend사이이기 때문에 연산자 오버로드와 멤버 함수 연산자 오버로드를 반드시 구분할 필요는 없습니다 .

전역 연산자 오버로드 를 선호하는 한 가지 이유 는 이항 연산자 의 오른쪽 에 클래스 유형이 나타나는 식을 허용하려는 경우 입니다. 예를 들면 :

Foo f = 100;
int x = 10;
cout << x + f;

이것은 전역 연산자 오버로드가있는 경우에만 작동합니다.

Foo 연산자 + (int x, const Foo & f);

전역 연산자 오버로드가 반드시 friend함수일 필요는 없습니다 . 의 비공개 멤버에 대한 액세스가 필요한 경우에만 필요 Foo하지만 항상 그런 것은 아닙니다.

관계없이 Foo다음과 같이 멤버 함수 연산자 오버로드 만있는 경우 :

class Foo
{
  ...
  Foo operator + (int x);
  ...
};

... 그러면 더하기 연산자 Foo의 왼쪽 에 인스턴스가 나타나는 표현식 만 가질 수 있습니다 .


3
+1은 회원 기능과 친구 기능이 아닌 회원 기능과 비회원 기능을 구분합니다. 오늘은 "글로벌 또는 네임 스페이스 범위"라고 말할 것 같습니다.
— Adrian McCarthy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.