C/C++ 관련 헷갈리기 쉬운 것 정리7(저장용,메모용, 윤성우 열혈 C++ 프로그래밍 정리 CH 10)

RisingJade의 개발기록·2022년 2월 23일
0

Chapter 10. 연산자 오버로딩 1


10-1. 연산자 오버로딩의 이해와 유형

operator+ 라는 이름의 함수

class Point{
private:
	int xpos,ypos;
public:
	Point(int x =0, int y=0) : xpos(x), ypos(y){}
    Point operator+(const Point &ref){//함수이름이 "operator+"!!
    	Point pos(xpos+ref.xpos, ypos+ref.ypos);
        return pos;
    }
}

int main(void){
	Point pos1(3, 4);
    Point pos2(10, 20);
    Point pos3 = pos1.operator+(pos2); // == pos1.operator+(pos2);
}
  • operator+라는 특이한 함수는 위와같이 쓸수도 있고, pos1 + pos2로 바로 쓸 수 있다.

연산자를 오버로딩 하는 두 가지 방법

  • 멤버함수에 의한 연산자 오버로딩
    • 위에서 보였던 pos1.operator+(pos2)와 같은 방법
  • 전역함수에 의한 연산자 오버로딩
    • 멤버함수가 아니라 전역 필드에 그냥 함수를 만들어서 쓸 수도 있다.
    • operator+(pos1, pos2)와 같은 형식으로 인식되며 정말 함수 호출이랑 크게 다를게 없다.
    Point operator+(const Point &pos1, const Point &pos2){
    	Point pos(pos1.xpos + pos2.xpos, pos1.ypos + pox2.ypos);
          return pos;
    }
    • 엄밀히 말하면 객체지향에는 전역(global)이라는 개념이 존재하진 않지만 C++은 C 스타일 코드구현이 가능한 언어이기 때문에 전역이라는 개념이 여전히 존재한다. 따라서 특별한 경우가 아니면 멤버함수 기반으로 연산자 오버로딩을 구현하자.

오버로딩이 불가능한 연산자의 종류

  • . : 멤버 접근 연산자
  • .* : 멤버 포인터 연산자
  • :: : 범위 지정 연산자
  • ?: : 조건 연산자(삼항 연산자)
  • sizeof : 바이트 단위 크기 계산
  • typeid : RTTI 관련 연산자
  • static_cast : 형변환 연산자
  • dynamic_cast: 형변환 연산자
  • const_cast: 형변환 연산자
  • reinterpret_cast: 형변환 연산자

위와 같은 연산자에 대해 오버로딩을 제한하는 이유는 C++의 문법규칙을 보존하기 위해서다. C++을 혼란스러운 언어로 만들 지 않기 위한 방편이다.

  • 반대로 멤버함수 기반으로만 오버로딩이 가능한 연산자
    • = : 대입 연산자
    • () : 함수 호출 연산자
    • [] : 배열 접근 연산자(인덱스 연산자)
    • -> : 멤버 접근을 위한 포인터 연산자

연산자를 오버로딩 하는데 있어서의 주의사항

  • 본래 의도를 벗어난 형태의 연산자 오버로딩 금지
    -> 상식적으로 pos1 + pos2면 멤버 별로 덧셈을 해서 그 결과로 객체 반환을 하는 연산이여야 된다. 엉뚱하게 pos2값만큼 pos1의 값을 증가시킨다던가 pos2값을 증가시키는 연산이 되서는 안된다.
  • 연산자 우선순위와 결합성은 바뀌지 않는다.
  • 매개변수의 디폴트 값 설정은 당연히 불가능하다.
  • 연산자의 순수 기능까지 빼앗을 수는 없다.
    -> operator+함수에 pos1*pos2와 같은 완전 순수 기능자체를 꼬아버리는 코드는 허용되지 않는다.

10-2. 단항 연산자의 오버로딩

증가, 감소 연산자의 오버로딩

  • ++ : 증가 연산자
  • -- : 감소 연산자
  • pos++ : operator++를 오버로딩하여 사용할 수 있다.
    -> pos.operator++() 이와 같은 형식으로 해석할 것이다.
Point& operator++()
{
	xpos+=1;
    yops+=1;
    return *this
}

위의 코드를 보면 return *this를 통해서 this(자기 자신을 가리키는 포인터)의 *역참조 즉, 객체자신을 반환함을 알수 있다. 또한 반환형이 참조형이므로 객체 자신을 참조 할 수 있는 '참조 값'이 반환된다.
만약 참조형이 아닌 Point형이 선언된다면, 객체 자신의 복사본을 만들어서 반환을 하게 된다!!!.

전위증가와 후위증가의 구분

  • ++pos -> pos.operator++() 이런 형태로 전위증가가 표현!
  • pos++ -> pos.operator++(int) 이런 형태로 후위증가가 표현!
    -> 여기서 (int)는 그냥 후위냐 전위냐를 구별하기 위해 넣는 값이지 정수랑 아무런 관련이 없다!
Point& operator++()// 전위 증가! 
{
	xpos+=1;
    yops+=1;
    return *this
}
const Point operator++(int) // 후위 증가!!
{
	xpos+=1;
    yops+=1;
    return *this
}
  • 후위 증가는 미리 객체를 하나 만들어서 그 증감 전 객체를 반환하는 것
  • 전위는 바로 내 자신을 증감시키고 나 자신을 반환한다.

반환형에서의 const 선언과 const 객체

Point& operator++(int)//전위 증가 바로 자기 자신을 리턴함으로 증감이 바로 적용됨
{
	xpos+=1;
    yops+=1;
    return *this
}

const Point operator++(int)// 
{
	const Point retobj(xpos, ypos); // const Point retobj(*this);
	xpos+=1;
    yops+=1;
    return *this
}
const Point operator--(Point &ref, int)
{
	const Point retobj(ref);
	xpos+=1;
    yops+=1;
    return *this
}
int main(void)
{
	Point pos(3,5);
    (pos++)++ // 컴파일 Error
    (pos--)-- // 컴파일 Error
    
}

위와 같은 컴파일 에러가 나는 이유를 알아보자
일반 기본적으로 ++, -- 둘다 후위 연산자이고 (pos++)가 먼저 계산 되면서
(const 임시 객체)++가 된다. 이는 (const 임시 객체).operator++()가 되고 이는 const 함수가 아니므로 호출할 수 없다.
--의 경우 매개변수로 참조자가 선언되었는데 이 참조자가 const가 아니기 때문에 컴파일에러가 난다.

profile
언제나 감사하며 살자!

0개의 댓글