💻 개인공부 💻/C | C++

[C++] 연산자 중복 (feat. 프랜드 함수)

공대생 배기웅 2020. 6. 19. 02:37
반응형

 

출처 => C++ How to Program (Detitel / Prenticehall) / 현재 그리고 미래지향적인 C++ 프로그래밍(장석우, 임정목 / 비앤씨에듀케이션)

https://beomnaegol.tistory.com/entry/C-%EC%97%B0%EC%82%B0%EC%9E%90-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%94%A9-3-%EC%A0%84%EC%9C%84%EC%A6%9D%EA%B0%90%EA%B3%BC-%ED%9B%84%EC%9C%84%EC%A6%9D%EA%B0%90
http://blog.daum.net/coolprogramming/73

 

연산자 중복의 정의

▶ 연산자 중복이랑 C++에서 제공하는 기본 타입이 아닌 클래스 타입(사용자가 정의하는 타입)에도 연산자를 사용할 수 있도록 하는 문법이다. 

▶ 예를 들어, 

cout<<2+3<<endl;
// 5

위의 소스코드는 컴파일이 가능하지만,

cout<<빨간색+노란색<<endl;
// 컴파일 오류!

이는 당연히 오류가 생긴다.

우리는 직관적으로 빨간색과 노란색을 섞으면 주황색이 나온다는 것을 알지만 컴파일러는 이런 사실을 알고 있지 않기 때문이다. 

따라서 컴파일러 내부에 정의되어 있지 않은 자료형의 연산도 제공할 수 있도록 하여 코드의 직관성, 가독성, 그리고 편의성을 제공하고자 만든 문법이 연산자 중복이다. 

 

연산자 중복의 특징은 다음과 같다.

▶ C++에 본래 있는 연산자만 가능하다. **나 %%와 같은 기괴한 형태의 연산자는 불가능!

▶ 연산자는 함수 형태로 구현이 된다.

▶ 반드시 클래스와 관계를 가져야 한다.

▶ 모든 연산자가 중복 가능하지 않는다. 가령, ::는 범위를 지정해주는 연산자이기 때문에 연산자 중복으로서 기능을 담당할 수가 없다.

 

연산자는 크게 2가지의 방법으로 구현이 가능하다. 

1. 외부 함수로 구현하고 클래스에 프랜드 함수로 선언

2. 클래스의 멤버함수로 구현

 

1. 외부 함수로 구현하고 클래스에 프랜드 함수로 선언하는 예제

#include<iostream>
using namespace std;

class Rect {
private:
	int width, height;
public:
	Rect(int width=0, int height=0) {
		this->width = width;
		this->height = height;
	}
	void show();
	friend Rect operator -(Rect a, Rect b);
};

Rect operator-(Rect a, Rect b) {
	Rect c;
	c.width = a.width - b.width;
	c.height = a.height - b.height;
	return c;
}

void Rect:: show() {
	cout << "width :" << width 
    << "," << "height" << height<<endl;
}
void main() {
	Rect a(5, 8), b(1, 3), c;
	c = a - b;
	c.show();
}

 

 

2. 클래스의 멤버함수로 연산자를 구현하는 예제

#include<iostream>
using namespace std;


class Rect {
private:
	int width, height;
public:
	Rect(int width = 0, int height = 0) {
		this->width = width;
		this->height = height;
	}
	Rect operator +(Rect r);
	void show();
};

Rect Rect::operator +(Rect r) {
	Rect c;
	c.width = this->width + r.width;
	c.height = this->height + r.height;
	return c;
}

void Rect::show() {
	cout << "width:" << width 
    << "," << "height:" << height << endl;
}

void main() {
	Rect a(5, 8);
	Rect b(1, 5);
	Rect c;

	c = a + b;
	c.show();
}

 

 

 

 

외부함수로 구현한 연산자와 클래스의 멤버함수로 구현한 연산자의 차이점

▶ 두 가지의 방법이 약간 다르다. 이를 설명하자면 다음과 같다.

 

▶ 위의 예시와 비교를 해보기에 앞서 1번 예시는 외부 함수로 구현하고 클래스에 프랜드 함수로 선언하는 방법이고, 2 번 예시는 클래스의 멤버함수로 연산자를 구현하는 예제임을 다시 한 번 강조한다.

▶ 먼저 위의 예시이다.

Rect operator-(Rect a, Rect b) {
	Rect c;
	c.width = a.width - b.width;
	c.height = a.height - b.height;
	return c;
}

Rect클래스의 연산자인 -, 그리고 그 연산자의 사용되는 객체 a와 b. Rect 클래스의 또 다른 예시인 c는 a의 가로 세로를 b의 가로 세로와 합한 값이다. 

▶ 위의 코드는 직관적이다. a의 가로 세로, b의 가로 세로를 합하고 그 합한 결과인 c라는 객체를 return 한다. operator 연산자가 Rect형이니 c로 return을 할 수 있다.

 

▶ 그 다음은 2번 예제의 소스코드이다. 

Rect Rect::operator +(Rect r) {
	Rect c;
	c.width = this->width + r.width;
	c.height = this->height + r.height;
	return c;
}

▶ 1번 예시는 Rect 클래스의 객체 a ,b가 다 있었던 반면에 이 소스코드는 r이라는 객체밖에 없다. 왜 그럴까?

 

 

▶ 위의 그림이 답해주고 있다. 클래스의 멤버함수로 구현하는 경우(2번 예시), 클래스 자신이 이미 1개의 인자 역할을 하기 때문에 1번처럼 2개로 객체를 지정하면 이 연산자 함수에 매개변수가 너무 많습니다는 오류 메시지가 발생하게 된다. 따라서 클래스의 멤버함수로 선언할 때는 위와 같이 해줘야 한다.

 

전위 연산자와 후위 연산자

▶ 조건문을 쓸 때 흔히 ++나 --를 사용한다. 이를 증감연산자라고 부르는데, 변수의 앞에 붙으면 전위증감, 뒤에 붙으면 후위증감이라고 부른다. 이는 연산자 오버로딩에서도 똑같이 적용되지만 구현을 하기 위해서는 특별한 법칙을 알아야 한다.

 

▶ 먼저 전위 연산자이다. 

#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++();
};
void Power::show() {
	cout << "kick=" << kick 
    << "," << "punch=" << punch << endl;
}
Power Power::operator++() {
	kick++;
	punch++;
	return *this;
}

void main() {
	Power a(3, 5), b;
	a.show();
	b.show();
	b = ++a;
	a.show();
	b.show();
}

 

 

 

▶ 다음은 후위 연산자이다. 

#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++(int x); //후위 ++연산자 함수 선언 
};
void Power::show() {
	cout << "kick=" << kick 
		<< "," << "punch=" << punch << endl;
}
Power Power::operator++(int x) {
	Power tmp = *this;
	//증가 이전 객체 상태를 저장
	kick++;
	punch++;
	return tmp;//증가 이전의 객체 return 
}

void main() {
	Power a(3, 5), b;
	a.show();
	b.show();
	b =a++;//후위 연산자 사용
	a.show();//a의 파워 1증가
	b.show();//b는 a가 증가되기 이전의 상태를 가짐
}

 

 

▶ 이 두 연산자 역시 차이점이 존재한다. 전위 연산자 함수와 후위 연산자 함수를 비교해보자.

//전위 연산자
Power Power::operator++() {
	kick++;
	punch++;
	return *this;
}

▶ kick과 punch를 각각 1만큼 증가시킨 뒤에, 그 증가가 된 객체를 return하는 것을 볼 수 있다. 

 

//후위 연산자
Power Power::operator++(int x) {
	Power tmp = *this;
	//증가 이전 객체 상태를 저장
	kick++;
	punch++;
	return tmp;//증가 이전의 객체 return 
}

▶ 그와 달리, 후위 연산자는 증가 이전의 객체를 tmp라는 Power 클래스의 또다른 객체에 저장하고, 증가를 시킨 뒤에 tmp를 return함을 알 수 있다.

728x90
반응형