오버로딩 그리고 Friend
모두의 코드 내용을 공부하고 정리한 내용입니다.
오버로딩
- 기존 정의되어 있는 것을 사용자가 원하는 것으로 재정의하는 것을 오버로딩이라 한다.
- 함수 이름은 같지만 매개변수가 다르면 다른 함수로 인식한다
void Func(int x) {} void Fucn(double x) {} // 두 함수는 다른 함수로 취급 => 매개변수가 다름
- 리턴 타입은 다른 함수라고 판단하는 기준이 되지 않는다.
- 매개변수 이름만 다른 것은 다른 함수라고 판단하지 않는다.
연산자 오버로딩
class A
{
public:
int x = 3;
int y = 10;
};
int main()
{
A object1;
A object2;
cout << object1 + object2 << endl; // 오류!
}
- 우리만의 클래스 객체 간의 연산이 필요하다면 연산자 오버로딩이 필요하다.
- 원하는 객체를 매개변수로 받아 연산을 하고 결과를 리턴하는 함수를 만들 수 있다.
int Add(const A& a, const A& b) {}
- 리턴타입 operator연산자(연산자가 받는 인자)
+
,-
,<<
등 특정 타입의 객체를 피연산로 들오면 우리가 정한 작업을 하게 미리 정의를 할 수 있다.- 이것이 연산자 오버로딩이다.
int operator+(const A& a, const A& b)
{
return a.x + b.x;
}
cout << object1 + object2;
오버로딩이 가능한 연산자자
- 산술 연산자
+
,-
,*
,/
,%
+=
와 같은 복합 연산자도 가능하다.- 멤버 함수로 사칙연산 오버로딩경우 리턴을 값으로 리턴해야 한다.
- 만약 레퍼런스로 리턴을 하면 생각했던 것과 다르게 작업한다.
A a = b + c + b;
이것은a = b * 2 + c
를 의도했을 것이다. - 하지만 실제로는
(b.plus(c)).plus(b)
로 작업이 되는데b
에는(b + c)
가 들어가고, 거기에 다시plus(b)
를 하게 된다면(b+ c) + (b + c)
가 수행되서 생각과 다르게 결과값이 나온다.
- 입출력 연산자
<<
,>>
- 단항 연산자
+
,-
,!
- 비교 연산자
>=
,==
,>
,!=
- 증감 연산자
++
,--
operator++();
: 전위 연산자ex) ++a
값이 바뀐 자기 자신을 리턴operator++(int x);
: 후위 연산자ex) a++
값이 바뀌기 이전의 객체를 리턴- 여기서 인자는 단순히 전위 증감 연산자와 구별하기 위한 척도이다.
- 첨자 연산자
[]
- 괄호 연산자
()
- 논리 연산자
&&
,||
- 형변환 연산자
(자료형)
,자료형()
- 대입 연산자
=
- 멤버 함수 대입 연산자의 경우 레퍼런스로 리턴해야 한다.
- 대입을 한 후에 해당 값으로 다시 연산을 수행하는게 아니기 때문이다.
- 대입 연산자는 디폴트로 정의되어 있지만 얕은 복사를 한다.
- 동적으로 메모리 관리하는 것이 있다면 직접 대입 연산자를 만들자
- 비트 연산자
<<
,>>
,&
,|
,^
- 포인터 관련 연산자
*
,&
,->
,->*
- 메모리 연산자
new
,new[]
,delete
,delete[]
오버로딩이 불가능한 연산자
?
조건연산자::
범위 지정 연산자sizeof
크기 연산자.
멤버 선택 연산자.*
포인터로 멤버 선택할 때 연산자
전역 함수 연산자 오버로딩 VS 멤버 함수 연산자 오버로딩
전역함수
class A
{
private:
int x;
public:
friend A operator + (const A& object1, const A& object2); // friend는 뒤에서 설명명
};
A operator + (const A& object1, const A& object2)
{
return A(object1.x + object2.x);
}
- A1 + A2
- A 타입의 두 매개변수를 받아 오버로딩 된 + 연산을 실행한다.
- 덧셈 결과를 A 타입으로 리턴한다.
- (A1 + A2) + A3
- A1 + A2의 덧셈 결과도 A 타입이니
- 덧셈 결과와 A3, 두 매개변수를 받아 오버로딩 된 + 연산을 실행한다.
- 덧셈 결과를 A 타입으로 리턴한다.
멤버 함수
class A
{
private:
int x;
public:
A operator + (const A& object)
{
return A(x + object.x);
}
};
- A1 + A2
- 자기 자신 : A1
- 인수 1개 : A2
- 덧셈 결과를 A 타입으로 리턴한다.
- (A1 + A2) + A3
- 자기 자신 : (A1 + A2) 덧셈 결과
- 인수 1개 : A3
- 덧셈 결과를 A 타입으로 리턴한다.
정리
- 위에서 잠깐 이야기를 했는데 반환값을 다시 사용하느냐 안 하느냐에 따라 반환값을 달리 했다
- 간단히 정리를 하자면
- 두 개의 동등한 객체 사이에서의 이항 연산자는 멤버 함수가 아닌 외부 함수로 오버로딩 하는 것이 좋다.
(예를 들어 operator+(const A&, const A&) const)
- 두 개의 객체 사이의 이항 연산자 이지만 한 객체만 값이 바뀐다던지 등의 동등하지 않는 이항 연산자는 멤버 함수로 오버로딩 하는 것이 좋다.
(예를 들어서 operator+= 는 이항 연산자 이지만 operator+=(const Complex&) 가 낫다)
- 단항 연산자는 멤버 함수로 오버로딩 하는 것이 좋다.
(예를 들어 operator++ 의 경우 멤버 함수로 오버로딩)
friend
-
A 클래스에 friend로 B클래스가 선언되었다면 B클래스는 A클래스의 private에 접근이 가능하다.
- 함수도 friend로 선언했다면 마찬가지로 private에 접근이 가능하다.
class A
{
private:
int x;
public:
int getA() const { return x; }
int& getA() { return x; }
friend void operator + (const A& object1, const A& object2); // A의 친구로 지정.
};
void operator+(const A& object1, const A& object2)
{
cout << object1.x + object2.x << endl; // friend이기에 직접 접근 가능
// cout << object1.getA() + object2.getA() << endl; // private이여서 직접 접근 못함
// 여기에서 getA()는 int getA() const { return x; }이다
// 인수가 const객체이기 때문
}
int main()
{
A a1(5);
A a2(7);
cout << a1.getA() + a2.getA() << endl;
// 여기에서 getA()는 int& getA() { return x; }이다
a1 + a2; // 오버로딩한 + 호출
}
- 원래는 private인 멤버 변수에 접근을 하지 못한다.
- 그래서 public 멤버 함수로 Get함수를 만들어 접근을 하여 사용한다.
- 하지만 friend를 사용하여 쉽게 접근할 수 있다.
- 반대로 A클래스는 B클래스의 private에 접근하지 못한다.
- 일방향적인 능력
- 그런데friend 사용으로 인해 private 의미가 없어진다.
- C++의 은닉성을 망친다.
- 분명히 필요할 경우에만 사용하자.
개인 공부 기록용 블로그입니다.
틀린 부분 있으다면 지적해주시면 감사하겠습니다!!
댓글남기기