개발/C++

14장 연산자 오버로딩

민돌이2 2021. 12. 9. 22:01

한성대학교 김설현교수님 강의내용을 바탕으로 작성함

 

연산자 오버로딩

정수형, 실수형, 문자형 등의 기본 자료형에 +,-,*,/,++,--등의 연산자를 적용할 수 있다.

하지만 개발자가 만든 자료형에는 이러한 연산자를 사용할 수 없다.

C++에서는 클래스에 함수로 정의한 후 사용할 수 있는데 이를 연산자 오버로딩(재정의)혹은 연산자 함수라고 한다.

 

문자열에서의 연산자 함수

string s1("Washington");
string s2("California");
cout << "The first character in s1 is " << s1[0] << endl;
cout << "s1 + s2 is " << (s1 + s2) << endl;
cout << "s1 < s2? " << (s1 < s2) << endl;

 

이렇게 사용해왔던 것들이 실제로는

string s1("Washington");
string s2("California");
cout << "The first character in s1 is " << s1.operator[](0) << endl;
cout << "s1 + s2 is " << operator+(s1, s2) << endl;
cout << "s1 < s2? " << operator<(s1, s2) << endl;

이렇게 함수로 재정의 되어있다고 생각하면 된다.

 

연산자 함수

1.오버로딩이 가능한 연산자는 아래와 같다.

+, -, *, /, %, ^, &, |, ~, !, =, <, >, +=, -=, *=, /=, %=, ^=, &=, |=, <<, >>, >>=, <<=, ==, !=, <=, >=, &&, ||, ++, --,

-*&, ,, ->, [], ( ), new, delete

2.C++에는 오버로딩 할 수 없는 4개의 연산자가 있다.

?:, ., .*, ::

3.대부분의 연산자는 멤버 함수나 일반 함수로 작성할 수 있지만, =, ( ), [], ->연산자는 멤버 함수로, << 연산자는 일반 함수로 작성해야 한다.

4.연산자 오버로딩에서 C++의 기존 연산 방법을 바꿀 수 없다.

 

아래와 같은 클라스가 있다고 하면

Rational.h

#ifndef RATIONAL_H
#define RATIONAL_H
#include <string>
using namespace std;

class Rational 
{
public:
	Rational();
	Rational(int numerator, int denominator);
	int getNumerator() const;
	int getDenominator() const;
	Rational add(const Rational& r2) const;
	Rational subtract(const Rational& r2) const;

private:
	int numerator;      //분자
	int denominator;  //분모
	int gcd(int n, int d);  //최대공약수
};
#endif

Rational.cpp

#include "Rational.h"
#include <cstdlib> // for the abs 
Rational::Rational()
{
	numerator = 0;
	denominator = 1;
}

Rational::Rational(int numerator, int denominator) 
{
	int factor = gcd(numerator, denominator); //최대공약수로 나누어 최저항으로 만듬
	this->numerator = ((denominator > 0) ? 1 : -1) * numerator / factor;
	this->denominator = abs(denominator) / factor;
}

int Rational::gcd(int n, int d) 
{
	int n1 = abs(n);
	int n2 = abs(d);
	int gcd = 1;
	for (int k = 1; k <= n1 && k <= n2; k++) {
		if (n1 % k == 0 && n2 % k == 0) //분자, 분모 모두 나누어 떨어지는 수  
			gcd = k;		//그 중 가장 큰 수를 찾음
	}

	return gcd;
}

int Rational::getNumerator() const {
	return numerator;
}
int Rational::getDenominator() const {
	return denominator;
}

Rational Rational::add(const Rational& r2) const // /𝑏 + 𝑐/𝑑 = (𝑎𝑑+𝑏𝑐)/𝑏𝑑
{  
	int n = numerator * r2.getDenominator() + denominator * r2.getNumerator();
	int d = denominator * r2.getDenominator();

	return Rational(n, d);
}

Rational Rational::subtract(const Rational& r2) const //𝑎/𝑏 - 𝑐/𝑑 = (𝑎𝑑−𝑏𝑐)/𝑏𝑑
{  
	int n = numerator * r2.getDenominator() - denominator * r2.getNumerator();
	int d = denominator * r2.getDenominator();

	return Rational(n, d);
}

main.cpp

#include <iostream>
#include "Rational.h"
using namespace std;

int main()
{
	Rational r1(4, 2);
	Rational r2(2, 3);
	Rational r3 = r1.add(r2); //연산자 오버로딩이 없기에 r1 + r2불가
	Rational r4 = r1.subtract(r2); //마찬가지로 r1 - r2불가

	// add() 함수와 subtract() 함수를 테스트 함
	cout << "r1 + r2 = " << r3.getNumerator() << "/" << r3.getDenominator() << endl;
	cout << "r1 - r2 = " << r4.getNumerator() << "/" << r4.getDenominator() << endl;

	return 0;
}

이런식으로 사용할 것이다.

하지만 연산자 오버로딩을 한다면

Rational add(const Rational& r2) const;

Rational Rational::add(const Rational& r2) const // /𝑏 + 𝑐/𝑑 = (𝑎𝑑+𝑏𝑐)/𝑏𝑑
{  
	int n = numerator * r2.getDenominator() + denominator * r2.getNumerator();
	int d = denominator * r2.getDenominator();

	return Rational(n, d);
}

위의 함수를

Rational operator+(const Rational& r2) const;

Rational Rational::operator+(const Rational& r2) const //add 함수 그대로
{  
	int n = numerator * r2.getDenominator() + denominator * r2.getNumerator();
	int d = denominator * r2.getDenominator();

	return Rational(n, d);
}

이렇게 구현할 수 있다.

연산자 오버로딩을 구현했다면, 아래처럼 사용할 수 있다.

Rational r3 = r1.add(r2);
Rational r3 = r1 + r2; //연산자 오버로딩

그외 <, <=, []등등 도 구현가능하다.

<연산자 오버로딩

bool operator<(const Rational& r1, const Rational& r2);

bool operator<(const Rational& r1, const Rational& r2) 
{
  	Rational temp = r1 – r2;
	if (temp.getNumerator() < 0) 
		return true;
	else return false;
}

[]연산자 오버로딩

int operator[](int index);

int Rational::operator[](int index) 
{
	if (index == 0)
		return numerator;
	else
  	  	return denominator;
}

 

<<연산자 오버로딩

Ration객체를 화면에 출력하려면 스트림 삽입 연산자(<<)를 오버로딩 하면 된다.

cout << r;

cout << r은 실제로 operator << (cout, r)와 동일하다.

operator << (cout, r);

첫 번째 피연산자가 Ration클래스가 아니라 ostream 클래스의 인스턴스이므로 Rational클래스의 멤버 함수가 아닌 일반 함수로 정의해야 한다.

또한 연속된 수식에서 <<연산자를 사용하려면 ostream의 참조를 반환해야 한다.

cout << r << "followed by" << r2;

Rational.h

friend ostream& operator<<(ostream& out, const Rational& r );

Rational.cpp

ostream& operator<<(ostream& out, const Rational &r) 
{
  	out << r.numerator << "/" << r.denominator;
  	return out;
}

 

cout 의 동작 방식

cout << r1 << r2<< r3;
cout << r2 << r3;
cout << r3;
cout; //무시

 

friend 함수와 friend 클래스

클래스의 private 멤버는 클래스 외부에서 접근할 수 없다.

경우에 따라 외부함수나 클래스가 클래스의 private 멤버에 접근하도록 허용하는 것이 편리할 때가 있다.

C++에서는 friend키워드를 사용한다.

728x90