사용자 지정 연산자
사용불가 사용자 지정 연산자
사용가능 사용자 지정 연산자
+, -, *
와 같은 산술 연산자
+=, -=
와 같은 축약형 연산자
>=, ==
와 같은 비교 연산자
&&
,||
와 같은 논리 연산자
->
나*
와 같은 멤버 선택 연산자 (여기서*
는 역참조 연산자 입니다. 포인터에서p
할 때 처럼)
++, --
증감 연산자
[]
(배열 연산자) 와 심지어()
까지 (함수 호출 연산자)
연산자 오버로딩
기본형 : (리턴 타입) operator (연산자) (연산자가 받는 인자)
이 방법 외에는 함수 이름으로 연산자를 넣을 수 없다.
Complex operator+(const Complex& c) const;
Complex operator-(const Complex& c) const;
Complex operator*(const Complex& c) const;
Complex operator/(const Complex& c) const;
Complex Complex::operator+(const Complex& c) const {
Complex temp(real + c.real, img + c.img);
return temp;
}
Complex Complex::operator+(const char* str) const {
Complex temp(str);
return (*this) + temp;
}
Complex(const char *str)
생성자만 남겨놓고, operator+(const char *str)
계열들을 모두 지워보시고 컴파일시 정상작동한다 왜냐면 놀랍게도 컴파일러는 생성자가 있다면 그 생성자를 찾아서 자동으로 암시적 변환을 수행하기 때문이다. 여기서 생기는 문제는 앞뒤가 바뀌면 안된다는점 즉
a = a + "-1.1 + i3.923"; // ①
이건 되지만
a = "-1.1 + i3.923" + a; // ②
이건 안된다는 사실
중요! 사칙연산에서는 레퍼런스를 리턴하는게 아닌 값을 리턴해야만 한다.
이유는 Complex a = b + c + b;
이런 식이 있을때 사용자는 아마 2 * b + c를 의도했을 텐데, 사실은 (b.plus(c)).plus(b) 이렇게 처리가 된다. 만약 레퍼런스 타입을 넣게 되면 b객체에 b+c 가 들어가게 되서 실제로는 반드시 값을 리턴해야만 합니다.
인자 값이 함수 내부에서 바뀌지 않는게 확실 할때 const키워드를 붙혀주는게 바람직하다.
상수함수란
객체 내부의 함수가 함수안에서 어떤 변수도 바꿀 수 없다. 이는 클래스에서만 존재하는 개념이고, const함수에서는 const가 붙은 함수만을 호출 할 수가 있다.
대입 연산자 함수
Complex& operator=(const Complex& c);
대입연산자 함수에는 레퍼런스를 리턴해야만 불필요한 복사를 막을 수 있다.
이유는 간단하게 설명해서 a=b=c;
인 경우에 b=c에서 b를 리턴해야지 a=b가 성공적으로 수행이 가능하기 떄문이다.
이때 타입을 리턴하지 않고 레퍼런스를 리턴하는 이유는 불필요한 복사를 막기 위해서이다.
디폴트 대입연산자
디폴트 복사 생성자가 있던 것 처럼 대입연산자 역시 디폴트로 존재한다. 그러나 디폴트 대입연산자 역시 얕은 복사를 수행한다.
Complex& Complex::operator+=(const Complex& c) {
(*this) = (*this) + c;
return *this;
}//객체의 내부상태를 변경하기 때문에 const 함수 아니다!!!
some_class a = b;
a의 복사 생성자가 호출되는 것
some_class a;
a = b;
기본 연산자 호출 후 대입연산자 함수가 실행 되는 것.
주의사항
friend 키워드
class A {
private:
void private_func() {}
int private_num;
// B 는 A 의 친구!
friend class B;
// func 은 A 의 친구!
friend void func();
};
단 여기서 B에서는 A에 접근 할 수 있지만 A에서 B는 접근이 안 된다.
이항 연산자
a = a + "-1.1 + i3.923"; // ①
이건 아무 문제 없이 a = a.operator+(”-1.1 + i3.923) 가 돼서 문제가 없다.
a = "-1.1 + i3.923" + a; // ②
이 경우는 문제가 발생한다.
이는 일부 연산자들에 대해서는 해당되지 않는데 대표적으로 [] 연산자 (첨자), -> 연산자 (멤버 접근), 대입 연산자 (=), () 함수 호출 연산자
들의 경우 멤버 함수로만 존재할 수 있다.
- 외부 함수로 정의 할 때 우리는 private 에 접근해야만 하기 때문에 그 함수를 friend로 선언해주어야 한다.
class complex{
private : //이것저것
public:
// 이제 이 함수는 Complex 의 private 멤버 변수들에 접근할 수 있습니다.
friend Complex operator+(const Complex& a, const Complex& b);//이건 멤버함수가 아닌 외부 함수이다.
};
Complex operator+(const Complex& a, const Complex& b) {
Complex temp(a.real + b.real, a.img + b.img);
return temp;
}
+
, -
, *
, /
들은 모두 외부 함수로 선언하는 것이 원칙
+=
, -=
같은 애들은 모두 멤버 함수로 선언하는 것이 원칙입출력 연산자 오버로딩 하기
std::cout << a;
는 std::cout.operator<<(a)
와 같다
std::cout
이 int
나 double
변수, 심지어 문자열 까지 자유 자재로 operator<<
하나로 출력할 수 있었던 이유는 그 많은 수의 operator<<
함수들이 오버로딩 돼있기 때문ostream
클래스에 다른 객체를 오버로딩하는 operator<<
연산자 함수를 추가할 수는 없다. (표준헤더 파일은 수정 불가)
해결 방법 : ostream
클래스 객체와 Complex
객체 두 개를 인자로 받는 전역 operator<<
함수를 정의
// 아마 Complex -- 다른 클래스에서 입출력 연산자를 friend 로 지정해줬다.
friend ostream& operator<<(ostream& os, const Complex& c);`
};
std::ostream& operator<<(std::ostream& os, const Complex& c)
{ // 연산자 오버로딩 할 새로운 함수 os 는 cout이 될것이며 자동으로 암시적 형변환이 된다고 생각하자.
os << "( " << c.real << " , " << c.img << " ) ";
return os;
}
friend
키워드를 남발하는 것은 썩 권장하지 않는다. 왜냐하면friend
키워드는 해당 함수나 클래스에게 자기 자신의 모든private
멤버 함수와 변수들을 공개하기 때문. 따라서 구현 디테일은 최대한 숨기라는 원칙을 지키기가 힘들어진다.
첨자 연산자 (operator[])
[]
를 오버로딩char& operator[](const int index);
char& 를 리턴하는 이유는 str[10] = 'c';
같은 명령을 수행하기 때문이다.
char& MyString::operator[](const int index) { return string_content[index]; }
Wrapper 클래스 - 타입 변환 연산자
Wrapper
클래스는 무언가를 포장하는 클래스라는 의미인데, C++ 에서 프로그래밍을 할 때 어떤 경우에 기본 자료형들을 객체로써 다루어야 할 때가 있다. 이럴 때, 기본 자료형들 (int, float
등등) 을 클래스로 포장해서 각각의 자료형을 객체로 사용하는 것만약 wrapper클래스에서 형을 감싼경우 모든 연산자를 오버로딩을 해야하겠지만 그것을 아주 쉽게 해결하고 하는 방법이 있다.
그 wrapper 클래스 내부에 만약 wrapper class 를 int를 wrapping한 클래스라면 operator int() {return data;} 이렇게 해주면된다.
operator (변환 하고자 하는 타입) ()
operator int()
operator int() { return data; }
#include <iostream>
class Int {
int data;
// some other data
public:
Int(int data) : data(data) {}
Int(const Int& i) : data(i.data) {}
operator int() { return data; }//이걸 해줌으로써 우리는 연산자 오버로딩을 하나하나 해주지 않아도 되는 것이다!!!!!
};
전위/후위 증감 연산자
전위 증감 연산자
operator++();
operator--();
A& operator++() {
// A ++ 을 수행한다.
return *this;
}
후위 증감 연산자
operator++(int x);
operator--(int x);
//인자 x 는 아무런 의미가 없습니다. 단순히 컴파일러 상에서 전위와 후위를 구별하기 위해 int 인자를 넣어주는 것이지요.
//실제로 ++ 을 구현하면서 인자로 들어가는 값을 사용하는 경우는 없습니다
//operator++(int);
//operator--(int); 사실 이렇게 해도 똑같다!
A operator++(int) {
A temp(A);
// A++ 을 수행한다.
return temp;
}//temp 객체를 만들어서 이전 상태를 기록한 후에, ++ 을 수행한 뒤에 temp 객체를 반환하게 됩니다.
후위 증감 연산의 경우 추가적으로 복사 생성자를 호출하기 때문에 전위 증감 연산보다 더 느리다!
Uploaded by N2T
'CPP' 카테고리의 다른 글
[cpp개념공부] 가상함수와 업 캐스팅 (0) | 2023.02.11 |
---|---|
[Cpp 개념공부] 상속 (함수 오버라이딩, 상속 접근지시자) (0) | 2023.02.10 |
[c++개념정리]암시적 변환, explicit, mutable (0) | 2023.01.27 |
[c++개념공부]복사 생성자 (깊은복사 얕은복사), 소멸자 (0) | 2023.01.27 |
[c++개념공부]함수 오버로딩, 생성자, default (0) | 2023.01.27 |