operator overloading __ 연산자 오버로딩

😎·2022년 12월 19일
0

CPP

목록 보기
22/46
post-thumbnail

operator overloading

이 부분을 어떻게 설명해야할지 매우 어렵다. 음… 그래서 이해한대로 기록해보려고 한다!

어떻게 연산자를 멤버 함수 또는 비멤버 함수로 선언하여 오버로딩 할 수 있을까? 먼저 연산자를 멤버 함수로서 사용하는 구문을 간단한 식을 통해 알아보자. 1 + 1 을 표기하는 방법은 여러 가지가 있다.

+ 1 1 // prefix
1 + 1 // infix
1 1 + // postfix

infix 표기법은 우리가 수학을 할 때 자주 봤던 표기법이라 익숙하다.
prefix 에 괄호를 추가해보자. +( 1, 1 ) 이렇게 보면 함수 표기법이 된다.
postfix 표기법은 스택 응용을 할 때 활용한다. 어떻게 활용한다는 이야기인지 아직 잘 모르겠다.


그 중 1 + 1 을 이렇게 표기하면 객체, 멤버 함수, 매개변수처럼 표기할 수 있다.
1.+( 1 ) 앞에 1은 객체, + 는 멤버 함수, 괄호 안에 1은 매개 변수가 된다.

(객체는 this 를 통해 자신을 가리킬 수 있으므로 매개 변수에 따로 넣어 주지 않아도 된다.
`1.+(1 + 1)` 이지만 풀어쓰면 `1.+(this, 1)` 이고, `this`는 생략하기 때문에 없어도 된다는 것이다.)

잘못된 지식일 수 있다... ( 과감한 지적 환영한다 )

예시

예시를 통해 다음을 이해하자.

  1. 연산자 오버로딩을 어떻게 만들어서 사용하는가
  2. 연산자 오버로딩의 반환값이 어떨 때 참조이고 어떨 때 값인지 알기
  3. 연산자 오버로딩을 활용할 때 언제 객체가 생성되고 소멸되는지 알기

먼저 헤더 파일에서 연산자 오버로딩 식을 보자.

Integer.class.hpp

#ifndef INTEGER_CLASS_HPP
# define INTEGER_CLASS_HPP

# include <iostream>

class Integer
{
    private:
        int _n;

    public:
        Integer(int const n);
        ~ Integer(void);
        int getValue(void) const;

[=연산자] Integer&    operator=(Integer const& rhs);
[+연산자] Integer    operator+(Integer const& rhs) const;
};

[<<연산자]std::ostream& operator<<(std::ostream& o, Integer const& rhs);

#endif

오버로딩한 코드 줄 맨 앞에 표시를 했다. 위에서는 =, +, << 연산자에 오버로딩을 했다.
신기한 것을 볼 수 있다.

❗️이 부분은 잘못된 지식이 있을 수 있다.

  • =<< 연산자 오버로딩의 반환형은 참조인데, + 연산자 오버로딩의 반환형은 이다.
    • + 연산자는 매개 변수로 값을 받아온다. 객체나 배열을 다루지 않고 값을 연산하기 때문에 객체를 생성해서 연산된 값을 반환해야한다.
    • =반환값이 자기 자신이다. 그렇기 때문에 함수 내부에서 this 포인터로 자신을 반환한다. 예를 들어 a = b 는 다음과 같이 표현될 수 있다. a.operator=(b), 이때 a 자신을 반환해야하기 때문에 *this 를 반환한다.
  • << 연산자를 클래스 밖에서 오버로딩 하는 이유는 추측해보자면, =, + 처럼 Integer 클래스의 객체가 아닌, std 네임 스페이스 안의 ostream 객체를 사용하기 때문에 Integer 클래스 내부에 오버로딩할 필요가 없다.

Integer.class.cpp

생성자와 소멸자, +, =, << 가 실행되면 각각 announce 를 해준다.

#include <iostream>
#include "Integer.class.hpp"

Integer::Integer( int const n ) : _n( n ) {
  std::cout << "Constructor called with valut " << n << std::endl;
  return;
}

Integer::~Integer( void ) {
  std::cout << "Destructor called with value " << this->_n << std::endl;
  return;
}

int Integer::getValue(void) const { return this->_n; }

Integer& Integer::operator=( Integer const & rhs ) {
  std::cout << "Assignation operator called from " << this->_n;
  std::cout << " to " << rhs.getValue() << std::endl;

  this->_n = rhs.getValue();

  return *this;
}

Integer Integer::operator+( Integer const & rhs ) const {
    std::cout << "Addition operator called with" << this->_n;
    std::cout << " and " << rhs.getValue() << std::endl;

    return Integer(this->_n + rhs.getValue());
}

std::ostream & operator<<(std::ostream & o, Integer const & rhs) {
    std::cout << "<< operation overload and 적용" << std::endl;
    o << rhs.getValue();
    return o;
}

main.cpp

결과 값과 함께 연산자 오버로딩과 객체의 생성 및 소멸 과정을 보자.

#include <iostream>
#include "Integer.class.hpp"

int main(void) {
  						  Integer x(30);
						    Integer y(10);
						    Integer z(0);

[1.<< 연산자]			std::cout << "Value of x : " << x << std::endl;
[2.<< 연산자]			std::cout << "Value of y : " << y << std::endl;
// 같은 메모리 주소를 가진 복사 생성자를 생성해서 값을 바꾼 후에 소멸
[3.= 연산자]      y = Integer(12);
[4.<< 연산자]     std::cout << "Value of y : " << y << std::endl;

[5.<< 연산자]     std::cout << "Value of z : " << z <<  std::endl;
[6.=,+ 연산자]    z = x + y;
[7.<< 연산자]     std::cout << "Value of z : " << z << std::endl;

    return 0;
}
  • main 함수에서 Integer 클래스로 x, y, z 객체를 생성했다. 각각 _n 변수에 30, 10, 0 을 대입했다.
    터미널 1 ~ 3줄을 보면 알 수 있다.

  • 1 ~ 2.<< 연산자를 활용한 << x<< y 를 보자.
    4, 5 번째 줄을 보면, 오버로딩 << 를 사용하는 것을 확인할 수 있다.

  • 3.= 연산자 를 활용한 y = Integer(12) 를 보자.
    6 번째 줄을 보면, 생성자를 통해 12 를 대입한 객체가 생성된 걸 확인할 수 있다.
    그 후 오버로딩 = 를 사용한다는 걸 7 번째 줄을 보면 알 수 있다. 10 대신 12를 대입한다.
    대입 후 오버로딩 = 연산이 종료되면서 앞서 생성했던 객체가 필요 없어지며 소멸된다.

  • 4 ~ 5.<<연산자1 ~ 2.<<연산자 와 같다.

  • 6.=, + 연산자 를 보자.
    터미널 11 ~ 14를 보면 + 연산자를 먼저 실행하고 = 연산자를 실행한다.

    • 이때 + 연산자새로운 객체를 생성하여 반환하기 때문에 12번째 줄에 생성자가 호출된다.
      그 후 =연산자 에서 0 대신 42를 대입한 후 생성했던 객체가 필요 없어지며 소멸(14줄)된다.
  • 7.<<연산자 는 이전과 똑같다. 터미널 15번째 줄이다.

  • 모든 과정이 끝나고 소멸자가 생성되며 프로그램이 종료된다.


어렵다

음... 아직 많이 어렵다. 아마 잘못된 지식이 많이 있을텐데, 혹시나 이 자료를 보게 된다면 부디 잘 정리 되어있는 다른 자료도 참고하시길 바란다. 그리고 나도 더 열심히 하자!


참고 자료

profile
jaekim

0개의 댓글