본문 바로가기
개발/C++

14장 연산자 오버로딩

by 민돌이2 2021. 12. 9.

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

 

연산자 오버로딩

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

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

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

'개발 > C++' 카테고리의 다른 글

C++ 클래스 메모리 할당 정리  (0) 2021.12.27
16장 예외처리  (0) 2021.12.09
15장 상속과 다형성  (0) 2021.12.09
13장 파일 입력과 출력  (0) 2019.05.21
12장 템플릿, 벡터, 스택  (0) 2019.05.13
11장 포인터와 동적 메모리 관리(후)  (0) 2019.05.09
11장 포인터와 동적 메모리 관리(전)  (0) 2019.05.08
10장 객체 지향 개념  (0) 2019.02.11

댓글