C++ 연산자 오버로딩(작성중)

mohadang·2022년 9월 24일
0

C++

목록 보기
6/48
post-thumbnail

연산자 오버로딩

  • 개인적으로는 잘 사용하지 않음

  • 연산자 오버로딩 종류

    • 단항 연산자
    • 이항 연산자
    • 함수 호출 연산자, ()
    • 첨자(subscript) 연산자, []
    • new
    • delete
    • delete[]
  • EX

class Vector
{
public:
  Vector operator+(const Vector& rhs) const;
};

Vector Vector::operator+(const Vector& rhs) const
{
  Vector sum;
  sum.mX = mX + rhs.mX;
  sum.mY = mY + rhs.mY;
  return sum;
}

Vector sum = v1 + v2;
//'v1' : 좌항
//'v2' : 우항
  • 연산자 역시 메서드라서 메서드 방식으로 호출 가능
Vector sum = v1 + v2;
Vector sum = v1.operator+(v2);

std::cout << number;
std::cout.operator<<(number);
  • 연산자 오버로딩 하는 방법

    1. 멤버 함수 방식
    2. 전역 함수 방식
  • 멤버 함수를 통해서만 오버로딩 가능 한것

    • =, (), [], ->
    • 보면 특징이 반드시 옆에 expression이 있어야 함.
      • exp = 10
      • exp(10);
      • exp[10]
      • exp->data;
  • operator 치환 과정

Vector result = vec1 + vec2;
  |
  V
Vector result = vec1.operator+(vec2)
  |
  V
[반환형] result = vec1.함수이름(인자);
  |
  V
Vector Vector::operator+(const Vector& rhs) const;
  • 전역 함수를 사용해야 하는 경우
// mX, mY 는 private이라서 접근 불가능
std::cout << vector1.mX << ", " << vector1.mY << std::endl;

// 모든 멤버에 Getter를 구현해주어야 하나, 귀찮다. cout 쓰기도 번거롭다
std::cout << vector1.GetX() << ", " << vector1.GetY() << std::endl;

//이렇게 하고 싶다!!
std::cout << vector1;

/*
'<<' 를 연산자 오버로딩 하기 위해서는 다음 함수를 
cout 클래스에 멤버 함수로 추가해야 하는데...
그러나 어떻게 하지 cout 은 내가 수정할 수 있는 코드가 아닌데

[cout.h]
void cout::operator<<(cconst Vector& rhs) const
{
      A
      |
      cout 클래스에 다음 멤버함수 추가 가능??? ... X
}
*/
  • 내가 접근 권한이 없는 클래스에는 멤버 함수를 만들 수 없으니 전역 함수로 만들어야 한다.
  • "좌변에 접근 권한이 없을 때는 전역 함수로 만들어야 한다."
    • "C#의 확장 메서드 개념과 비슷..."
  • 전역 함수
    • 이 전역 함수가 잘 동작할 까??
    void operator<<(std::ostream& os, const Vector& rhs)
    {
      os << rhs.mX <<", " << rhs.mY
    }  
    • 위 코드의 문제
      1. 이 전역함수를 어디에 넣지???
      2. 이 함수가 Vector 클래스의 private 멤버를 어떻게 읽지
    • 해답은 'friend'

friend 함수

class X
{
  friend void Foo(X& x);
private:
  int mPrivateInt;
};

void Foo(X& x) // 전역 함수
{
  x.mPrivateInt += 10;
}
  • friend 함수는 '멤버 함수가 아님'.
  • 하지만 다른 클래스의 private 멤버에 접근할 수 있음.
  • 조금 덜 완벽한 Vector 예제
// [Vector.h]
class Vector
{
  // 지정자에 상관 없다, 어차피 frined로 모두 접근 가능할 텐데
  // 주로 클래스 상단에 위치 시킨다.
  // Vector에 operator를 지정
  friend void operator<<(std::ostream& os, const Vector& rhs);
}; 

// [Vector.cpp]
void operator<<(std::ostream& os, const Vector& rhs) const
{
  // friend로 지정 되었기 때문에 private 멤버에 접근 가능
  os << rhs.mX << ", " << rhs.mY;
}

Vector vector1(10, 20);
std::cout << vector1;
  • 문제 발생
std::cout << vector1 << endl;
// std::cout << vector1 에 대한 반환형이 없어서 
// << endl 구문에서 컴파일 에러가 발생 한다.  
// << endl 문제 처리하기 위해서는 반환형 처리 필요
std::ostream& operator<<(std::ostream& os, const Vector& rhs)
{
  os << rhs.mX << ", " << rhs.mY;
  return os;  //뒤에 올 녀석들도 출력하게 하기 위해 반환
}    

//std::ostream& os 는 const가 아니다, ostream의 operator<<() 함수가 const 함수가 아니기 때문에...
// 때문에 반환값 std::ostream& 도 const가 아니다.    

연산자 오버로딩과 const

  • 비 멤버함수에는 const를 사용할 수 없다.

    • 어떤 연산자 오버로딩에 const를 사용해야 하는가 ?
    • 어떤 연산자 오버로딩에 const를 사용하면 안되는가 ?
  • const를 쓰는 이유?

    • 기본적으로 시작은 const로 시작. 그 다음 const를 사용하면 안되는 경우 const를 빼는 방식으로
    • 멤버 변수의 값이 바뀌는 것을 방지
    • 최대한 많은 곳에 const를 붙일 것
    • 지역 변수에 까지도 모든 회사가 이 코딩 표준을 따르지는 않음 지역 변수까지 붙이는 것도 좋음
    • Vector operator+(const Vector& rhs) const;
      • 함수에 붙은 const : 좌항에 대한 const
      • 인자에 붙은 const : 우항에 대한 const
  • const&를 사용하는 이유?

    • 불필요한 객체의 사본이 생기는 것을 방지
    • 멤버 변수가 바뀌는 것도 방지
    • Vector operator+(const Vector& rhs) const;
    • std::ostream& operator<<(const std::ostream& os, const Vector& rhs);
      • 잘못된 예!!!, os는 const 객체로 받지만 os의 operator<<() 함수가 const 함수 가 아니기에 호출 불가능
      • os << rhs.mX 사용시 에러
      • 에러 해결하기 위해서는 const를 제거하고 os 객체를 받아야함

const를 사용하지 않는 경우

  • 객체 복사가 없음
vector1 += vector2
vector1 = vector1.operator+=(vector2);
Vector& operator+=(const Vector& rhs);
  • 여러 연산자를 붙여서 사용 할 수 있음
class Vector {
public:
  int m_x;
  Vector& operator+=(const Vector& rhs) {
    m_x = m_x + rhs.m_x;
    return *this;//참조 반환할 때는 이렇게 넘긴다.
  }
};

int main() {
  Vector vec1;
  vec1.m_x = 1;

  Vector vec2;
  vec2.m_x = 2;

  Vector vec3;
  vec3.m_x = 3;

  vec1 += vec2 += vec3;//chaining

  std::cout << vec1.m_x << std::endl; 

  return 0;
}    
  • 이렇게 연속적으로 붙이는걸 막고 싶다면 반환형의 const를 제거하고 인자의 const를 입력하면 된다.
const Vector& operator+=(Vector& rhs)

vector1 += vector2 += vector3

// vector2 += vector3 연산하고 반환한 것이 const Vector&인데
// vector1에서 받는 인자 타입은 Vector& rhs 로 받으니 에러가 발생해서 chaining을 못 한다.    

operator=

  • 복사 생성자와 비슷한데 추가적으로 메모리를 해제 해주어야할 수 도 있다.
MyString a = b;//복사 생성자
a = c;// 이때 a가 가지고 있던 메모리를 해제해주어서 c에 대한 deep copy를 해주어야 한다.
  • 복사 생성자를 구현하였다면 대입 연산자도 구현해 주어야 한다.
Vector& operator=(const Vector& rhs)
{
  mX = rhs.mX;
  mY = rhs.mY;
  return *this;// 자기 자신을 넘겨야 chaining이 가능
}

연산자 오버로딩을 남용하지 말 것

  • Vector vector = vec1 << vec2;
    • 이게 무슨 의미???...
  • Vector vector = vec1 * vec2;
    • cross product or hadamard product...
    • 두가지 모호한 표현을 나타내기도 한다.
  • 헤깔릴 때는 함수로 만들어서 의미를 명확하게 만들어야 한다.
  • CrossProduct(vec1);, HadamardProduct(vec1);
profile
mohadang

0개의 댓글