▣ 연산자 다중정의?
● 연산자 다중정의란?
▷ C++에 정의된 연산자를 사용자가 선언한 클래스 객체에서 사용할 수 있게 정의하는 것을 말한다.
● 연산자 다중정의 규칙
▷ 연산자의 의미를 임의로 바꾸면 안된다. 예를 들어, 덧셈을 뺄셈으로 바꾸는 행위는 안된다.
▷ '연산자의 우선순위'나 '피연산자 수'와 같은 연산자의 고유한 특성이 유지되도록 만든다.
● 연산자 다중정의를 사용하는 경우
▷ 클래스의 객체 간 대입/이동 대입 연산자의 경우
▷ 수치형 객체의 산술 연산자
▷ 두 객체를 비교하기 위한 관계 연산자
▷ 스트림 입출력을 위한 >> 와 << 연산자
● 연산자 다중정의를 사용할 수 없는 경우
▷ 멤버 선택 연산자 - .
▷ 멤버에 대한 포인터 연산자 - .*
▷ 유효범위 결정 연산자 - ::
▷ 조건 연산자 - ? :
● 연산자 다중정의 위치
1) 클래스의 멤버로 정의
▷ 연산자 구현 과정에서 객체 멤버에 접근 가능하다.
2) 클래스 외부에서 정의
▷ 클래스 내부에서 정의할 수 없는 경우에 이렇게 정의하는데 클래스의 멤버는 아니라서 객체의 private 멤버는 사용할 수 없다.
▣ 단항 연산자 다중정의
● 단항 연산자
▷ 피연산자가 1개인 연산자를 의미하고 전위 표기법과 후위 표기법으로 나뉜다.
A = 10일 때 단항 연산자 수식 | 실행 결과 | |
A | B | |
B = ++A ; | 11 | 11 |
B = A++ ; | 11 | 10 |
B = --A ; | 9 | 9 |
B = A-- ; | 9 | 10 |
● 단항 연산자 다중정의 형식
▷ 전위표기법 단항 연산자 다중정의
ReturnClass ClassName::operator opSymbol()
{
.....
}
//opSymbol: ++, -- 등 단항 연산자
// 형식 매개변수는 비어져있다.
▷ 전위표기법 단항 연산자 다중정의 예시
class Sample1 {
int a;
public:
/*생성자*/
Sample1(int n = 0) : a(n) { }
/*단항연산자 다중정의*/
Sample1& operator ++ () { //형식 매개변수 비워놓기
++a;
return *this; //자기 자신 객체(Sample1)를 return하는 것
}
int meThod() const {return a};
};
Sample1 h;
cout << (++h).meThod() << endl; // 1 출력
▷ 후위표기법 단항 연산자 다중정의
형식 매개변수로 int를 써서 전위가 아닌 후위 표기법임을 나타낸다.
ReturnClass ClassName::operator opSymbol(int)
{
.....
}
//opSymbol: ++, -- 등 단항 연산자
// 형식 매개변수로 int를 써서 전위가 아닌 후위 표기법임을 나타낸다.
// 꼭 정수만 전달할 필요는 없다.
▷ 후위표기법 단항 연산자 다중정의 예시
class Sample2 {
int a;
public:
/*생성자*/
Sample2(int n = 0) : a(n) { }
/*단항연산자 다중정의*/
Sample2& operator ++ (int) { //형식 매개변수 int --> 후위표기
Sample2 tmp(*this)
++a;
return tmp; //후위 표기이기 때문에 객체가 변화되기 이전의 값을 return하고 객체 자체는 변화됐다.
}
int meThod() const {return a};
};
Sample2 h;
cout << (h++).meThod() << endl; // 0 출력
● 단항 연산자 예제
▷ 예제 - Pencils 클래스
멤버함수 | 설명 |
Pencils() | 생성자, 0으로 초기화 |
Pencils(int n) | 생성자, n을 타와 낱개로 변환 |
Pencils(int d, int n) | 생성자, d타 n자루로 초기화 |
Pencils& operator ++() | 전위 표기 ++ 연산자 |
Pencils operator ++(int) | 후위 표기 ++ 연산자 |
void display() | 내용 출력 |
데이터 멤버 | 설명 |
int dozens | 타 수 |
int np | 낱개의 수 |
▷ Pencils 클래스 - Pencils.h
#ifndef PENCILS_H_INCLUDED
#define PENCILS_H_INCLUDED
using namespace std;
class Pencils {
int dozens;
int np;
public:
Pencils() : dozens(0), np(0) {};
Pencils(int n)
{ dozens = n / 12; np = n % 12; }
Pencils(int d, int n) : dozens(d), np(n) {}
/*연산자 다중정의*/
// ++ 전위 연산자
Pencils& operator ++ () { //객체의 값이 연산에 의해 바뀌므로 const 설정하지 않는다.
if (++np >= 12) //낱개를 1 증가시키고 결과가 12보다 크면
++dozens, np = 0; // 타 수를 1 증가시키고, 낱개는 0으로 지워준다.
return *this; // 증가된 결과 반환
}
// ++ 후위 연산자
Pencils operator ++ (int) {
Pencils tmp(*this); //현재 객체 보존
if (++np >= 12) // 낱개를 1 증가시키고 결과가 12보다 크면
++dozens, np = 0; // 타 수 1 증가시키고 낱개 0
return tmp; // 보존된 객체 반환
}
void display() const //출력하면서 객체 멤버 값이 변하지 않도록 const 선언한다.
{
if (dozens) {
cout << dozens << "타" ;
if (np) cout << np << "자루"; //np가 0이 아닐 때만 출력하게끔
cout << endl;
}
else
cout << np << "자루" << endl;
}
};
#endif
▷ Pencils 클래스 - main.cpp
#include <iostream>
#include "Pencils.h"
using namespace std;
int main()
{
Pencils p1(7, 7);
Pencils p2(123);
p1.display();
(++p1).display();
p1.display();
cout << endl;
p2.display();
p1 = p2++;
p1.display();
p2.display();
return 0;
}
▷출력 결과
▣ 이항 연산자 다중정의
● 이항 연산자 다중정의 형식
연산자를 사용하는 객체 자신이 왼쪽 피연선자, 매개변수 arg가 오른쪽 피연산자
ReturnClass ClassName::operator opSymbmol(ArgClass arg)
{
....
}
//opSymbol은 +, -, *, /, %, ||, && 등 이항 연산자 기호
//연산자를 사용하는 객체 자신이 왼쪽 피연선자, 매개변수 arg가 오른쪽 피연산자
▷ 이항 연산자 예시 - (복소수 객체 + 복소수 객체의 덧셈 연산자)
/* complex2Obj1 + complex2Obj2 수식 구현을 위한 연산자 정의*/
Complex2 Complex2::operator + (const Complex2 &c) const
{
Complex2 tmp(*this);
tmp.rPart += c.rPart;
tmp.iPart += c.iPart;
return tmp;
}
// 위와 동일하게 아래와 같이 간단하게 작성할 수도 있다.
Complex2 Complex2::operator + (const Complex2 &c) const
{
return Complex2(rPart + c.rPart, iPart + c.iPart);
//임시 객체를 만들어 return. 객체 이름이 따로 없다.
}
▷ 이항 연산자 예시 - (복소수 객체 + 실수의 덧셈 연산자)
동일한 + 연산자에 대해 다른 정의를 할 수 있다. 다중정의니까~
/* complex2Obj1 + 3.0 수식 구현을 위한 연산자 정의*/
Complex2 Complex2::operator + (double r) const
{
return Complex2(rPart + r, iPart);
}
위 연산자 정의의 경우 이렇게 연산자를 정의하지 않아도 Complex2(double r = 0, double i = 0)의 생성자를 통해 double값이 묵시적으로 Complex2 객체로 형변환되어 객체간 덧셈 연산자 정의를 통해 수식 처리가 가능하다.
▷ 이항 연산자 예시 - (실수 + 복소수 객체의 덧셈 연산자)
- 왼쪽 피연산자가 실수라서 Complex2 클래스 멤버로는 연산자를 정의할 수 없다.
- 그래서 클래스에 안 속하는 외부 별도 연산자로 정의해야 한다.
/* 3.0 + complex2Obj1 수식 구현을 위한 연산자 정의*/
Complex2 operator + (double r, const Complex2 &c)
{
return Complex2(r + c.rPart, c.iPart);
// c.rPart와 c.iPart는 클래스에 속해있는 private 멤버라서 오류가 발생한다.
}
c.rPart와 c.iPart는 클래스에 속해있는 private 멤버라서 오류가 발생한다. 이 오류를 해결하기 위한 방법은 2가지가 있다.
(Solution 1) Complex2에 private 멤버를 액세스할 수 있는 멤버함수를 따로 정의해준다.
class Complex2 {
double rPart, iPart;
public:
.....
double real() const {return rPart;}
double imag() const {return iPart;}
};
Complex operator + (double r, const Copmlex2 &c)
{
return Complex2(r + c.real(), c.imag());
}
(Solution 2) Complex2에서 다중정의된 연산자를 friend로 선언해준다.
class Copmlex2 {
double rPart, iPart;
public:
....
friend Copmlex2 operator + (double r, const Compelx2& c);
};
Complex2 operator + (double r, const Copmlex2 &c)
{
return Complex2(r + c.rPart, c.iPart);
}
▷ 이항 연산자 예시 - (복소수 객체의 복합 대입 연산자)
반환형은 Complex2&으로 자신에 대한 참조를 반환한다.
/* complex2Obj1 += complex2Obj2 수식 구현을 위한 연산자 정의*/
Complex2& Complex2::operator += (const Complex2 &c)
{
rPart += c.rPart;
iPart += c.iPart;
return *this;
}
▣ 스트림 출력 연산자 다중정의
● 스트림 출력 연산자(<<) 다중정의
▷ << 연산자 정의할 위치
Complex2 c(1.0, 2.0);
cout << c;
- 왼쪽 피연산자인 cout은 표준 C++라이브러리에서 정의한 것이므로 Complex2의 객체가 아니라 일반적으론 수정이 불가능하다.
- 따라서 클래스에 속하지 않는 외부 별로 연산자로 정의해야만 한다.
- 그리고 << 연산자가 Copmlex2 객체의 private 멤버에 접근 가능하게 friend로 지정한다.
▷ << 연산자 반환값
cout << "a에는 다음의 값이 저장되어 있다." << a;
● 스트림 출력 연산자 다중정의 예시
class Complex2 {
public:
Complex2(double r=0, double i=0):rPart(r), iPart(i) {}
Complex2 operator + (const Complex2 &c) const {
Complex2 tmp(*this);
tmp.rPart += c.rPart;
tmp.iPart += c.iPart;
return tmp;
}
/* << 연산자 다중정의 */
friend ostream& operator<<(ostream &os, const Complex2 &c);
};
/*왼쪽항 cout(os) || 우측항 대입할 변수(c)*/
ostream& operator<<(ostream& os, const Complex2& c)
{
os << "(" << c.rPart;
if (c.iPart > 0)
os << "+j" << c.iPart;
else if (c.iPart < 0)
os << "-j" << -c.iPart;
os << ")";
return os;
}
#include <iostream>
#include "Complex2.h"
using namespace std;
int main()
{
Complex2 a(12, 34);
Complex2 b(10, -12);
cout << a << " + " << b << " = " << a + b << endl;
return 0;
}
▷출력 결과
'Programming > C++' 카테고리의 다른 글
C++ 언어 기초 (11) - 상속; 기초/파생클래스, 접근 제어, final, name binding (0) | 2020.09.03 |
---|---|
C++ 언어 기초 (10) - 연산자 다중정의 II (0) | 2020.09.03 |
[C++] 위임 생성자, 초기화 리스트 생성자 (0) | 2020.08.29 |
[C++] 복소수 Complex 연산 클래스 만드는 방법 (0) | 2020.08.28 |
[C++] 자료구조 스택 Stack 구현하기 (0) | 2020.08.28 |