연산자 중복
연산자 중복이란 이미 역할이 정해져 있는 연산자에 다른 의미를 부여하는 것을 말합니다.
예를 들어, '+' 연산자를 생각해보면 수학에서는 + 연산의 연산대상에 값이 오지만 실제 세계는 " 8분 음표 + 8분 음표 = 4분 음표"와 같이 다양한 것들이 오고 우리는 그것을 이상하게 여기지 않습니다. 이런 것을 반영하고 연산자의 쓰임을 늘리기 위해 연산자 중복이 존재합니다.
연산자 중복까지 공부하셨다면 string 클래스를 아실겁니다. string을 +연산자로 더하면 문장이 합쳐집니다. +연산자가 string 클래스가 연산대상으로 왔다는 것을 확인하고 적절한 연산을 해주는 것입니다.
연산자 중복 함수를 만드는 방법
다음은 연산자 중복 함수선언 형식입니다.
리턴타입 operator 연산자(매개변수리스트); // 연산자중복 함수선언 형식
연산자 중복은 많은 예제를 해보고 직접 코딩해보는 것이 중요하다고 생각해서 많은 예제를 들고와봤습니다. 그리고 연산자 중복을 참조 리턴을 활용하는 반환, 임시객체를 활용하는 반환, 클래스 멤버함수를 프렌드 멤버함수로 구현해보는 것 다양하게 구현해보는 것도 앞서 배운 내용들을 복습하는 데에 도움이 됩니다.
Ex1) == 이항연산자 중복
#include<iostream>
using namespace std;
class Power {
int kick;
int punch;
public:
Power(int kick = 0, int punch = 0) {
this->kick = kick;
this->punch = punch;
}
void show();
bool operator==(Power op2);
};
void Power::show() {
cout << "kick =" << kick << ',' << "punch=" << punch << endl;
}
bool Power::operator==(Power op2) {
if ((this->kick == op2.kick) && (this->punch == op2.punch)) return true;
else return false;
}
int main() {
Power a(3, 5), b(3, 5);
a.show();
b.show();
if (a == b)cout << "두 파워가 같다." << endl; //a.==b;
else cout << "두 파워가 같지 않다." << endl;
}
==연산자를 이용해서 객체의 모든 멤버변수의 값을 비교한 후 같다면 true값을 반환합니다. 반환형도 bool형이니 반환에 대해 깊이 생각할 필요도 없어 단순비교만을 해주면 되므로 어렵지 않은 연산자 중복 예시입니다.
Ex2) += 이항연산자 중복(this포인터를 활용한 반환)
#include<iostream>
using namespace std;
class Power {
int kick;
int punch;
public:
Power(int kick = 0, int punch = 0) {
this->kick = kick; this->punch = punch;
}
void show();
Power& operator+=(Power op2);
};
void Power::show() {
cout << "kick =" << kick << ',' << "punch=" << punch << endl;
}
Power& Power::operator+=(Power op2) {
kick = kick + op2.kick;
punch = punch + op2.punch;
return *this;
}
int main() {
Power a(3, 5), b(4, 6), c;
a.show();
b.show();
c = a += b;
a.show();
c.show();
}
+= 연산자 중복에서는 참조리턴을 활용하였습니다. 위에서 += 연산자를 호출한 객체는 a이므로 kick, punch는 a 객체의 멤버변수가 됩니다. 그리고 반환을 this포인터로 하고 있습니다. 즉 a객체의 공간에 해당하는 별명을 반환하는 것입니다. 그래서 a+=b의 결과로 객체가 반환이 되었고 c객체에 대입할 수 있는 것입니다.
Ex3) += 이항연산자 중복(임시객체 생성을 통한 반환)
#include<iostream>
using namespace std;
class Power {
int kick;
int punch;
public:
Power(int kick = 0, int punch = 0) {
this->kick = kick; this->punch = punch;
}
void show();
Power& operator+=(Power op2);
};
void Power::show() {
cout << "kick =" << kick << ',' << "punch=" << punch << endl;
}
Power Power::operator+=(Power op2) {
Power tmp; //change
tmp.kick = this->kick + op2.kick;
tmp.punch = this->punch + op2.punch;
return tmp; //change
}
int main() {
Power a(3, 5), b(4, 6), c;
a.show();
b.show();
c = a += b;
a.show();
c.show();
}
operator 함수를 다른 반환방법으로 구성해보았습니다. 첫 번째 방법은 this포인터를 활용하여 참조리턴을 하였으므로 객체를 반환하는 과정에서 복사비용을 아낄 수 있었습니다. 하지만 위와 같은 방식으로 임시객체를 형성하여 리턴을 하면 복사비용(시간,메모리)이 더 들어간다는 것을 유념하여야 합니다.
1-3)단항연산자 중복
이항연산자 중복을 구현할 수 있다면 단항연산자 중복을 구현하는 것도 어려운 것은 아닙니다. ' ! ', ' ~ ' 와 같이 앞에만 붙을 수 있는 단항연산자가 있는 반면, ' ++ ', ' -- '와 같이 앞,뒤 둘 다 붙을 수 있는 연산자가 있습니다. 이를 구분하여 구현할 수 있어야 하므로 관련 예시로 알아보도록 하겠습니다.
#include<iostream>
using namespace std;
class Point {
private:
int x, y, z;
public:
Point();
Point& operator++(); //전위 ++ 연산자 중복
Point operator++(int k); //후위 ++연산자 중복
void show() { cout << "x : " << x << " y : " << y << " z : " << z << endl; }
};
Point::Point() {
x = y = z = 0;
}
Point& Point::operator++() {
++x; ++y; ++z;
return *this;
}
Point Point::operator++(int k) {
Point tmp = *this;
x++; y++; z++;
return tmp;
}
int main() {
Point a;
Point c;
a.show();
c.show();
c= ++a;
a.show();
c.show();
c = a++;
a.show();
c.show();
}
컴파일러가 연산자중복을 인식하는 방법
코드 | 컴파일러 변환 코드 | |
== | a == b | a.==b |
+= | a += b | a.+=b |
+ | a + b | a.+b |
++(전위) | ++a | a.++() |
++(후위) | a++ | a.++(int) |
+(수가 앞에 있을 때) | 2 + a | + (2 , a) |
컴파일러가 코드를 어떻게 변환하여 연산자 중복을 수행하는 지 알면 매개변수를 어떻게 정해야 하는 지 이해가 되어서 좋더라구요. 여러분들도 이렇게 알아가시면 안 까먹으실 것 같습니다.
예를 들어 == 이 연산자 중복이 되었고, a==b는 a.==b로 변환이 되었습니다. 우리가 객체의 멤버함수를 호출할 때 ' . ' 연산자를 많이 사용했지않습니까?? 그걸 똑같이 이용하는 것입니다. 즉 "a.==b는 a 클래스의 함수이름이 ==인 것을 호출하는 데 매개변수는 b다" 이렇게요.
a . == b --> a 클래스의 == 함수를 이용하는 데 그 때 매개변수가 b다.
a.setPoint(b) --> a클래스의 setPoint함수를 이용하는 데 그 때 매개변수가 b다.
프렌드를 이용한 연산자 중복
위에서 구현한 연산자 중복 방식은 모두 클래스의 멤버함수를 통해서 구현한 것입니다. 대부분 클래스의 멤버함수로 구현하는 것이 좋습니다. 하지만 그럴 수 없는 case가 있는데요.
b = 2 + a;
위에서 컴파일러가 변환하는 방식을 이해하셨다면
2 + a --> 2. + a가 되어야 하는 데 숫자 2의 멤버함수는 없죠. 따라서 이를 .+(2, a)로 변환합니다. 특정 멤버함수가 아니고 +가 함수이름인 외부함수에 매개변수가 2개인 함수를 호출하는 것이죠. 그래서 외부함수를 정의 한 다음 a라는 객체의 멤버에 접근해야하므로 클래스안에 friend를 선언해 접근권한을 주는 것입니다.
Ex4) 반드시 프렌드함수로 연산자 중복을 구현해야하는 예시
#include<iostream>
#include<string>
using namespace std;
class Book {
private:
string title;
int price, pages;
public:
Book(string title = "", int price = 0, int pages = 0) {
this->title = title; this->price = price; this->pages = pages;
}
void show() {
cout << title << ' ' << price << "원 " << pages << " 페이지" << endl;
}
string getTitle() { return title; }
friend bool operator <(string b, Book as);
};
bool operator <(string b, Book as) {
if (b[0] < as.title[0])return true;
else return false;
}
int main() {
Book a("청춘", 20000, 300);
string b;
cout << "책 이름을 입력하세요>>";
getline(cin, b);
if (b < a) cout << a.getTitle() << "이 " << b << "보다 뒤에 있구나!" << endl;
}//명품 C++ 7장 367p 4번문제
'Programming > C,C++' 카테고리의 다른 글
오버라이딩을 통한 다형성의 실현(virtual 개념, 오버라이딩 개념) (0) | 2022.03.01 |
---|---|
참조리턴 (0) | 2022.02.23 |
복사생성자 (0) | 2022.02.23 |
static 멤버(정적멤버) (0) | 2022.02.21 |
함수중복(fuction overloading) (0) | 2022.02.19 |