[전공]

[명품 C++] 07 프렌드와 연산자 중복

danhan 2022. 6. 5. 05:35

출처 :

  • 명품 C++ Programming (저자 황기태)
  • 객체지향프로그래밍
  • 블뎁블뎁

프렌드 함수

클래스의 멤버로 선언하긴 좀 그렇고, 내부에 접근하게 만들고는 싶을 때

  • 클래스의 멤버 함수가 아닌 외부 함수
    • 클래스 외부에 선언된 전역 함수
    • 다른 클래스의 멤버 함수
    • 다른 클래스의 모든 멤버 함수
    → 외부에서도 private 에 접근할 수 있게 된다.
  • class Rect { // 전역 함수 equals()를 Rect 클래스에 프렌드로 선언 friend bool equals(Rect r, Rect s); }; class Rect { // RectManager 클래스의 equals() 멤버 함수를 Rect 클래스의 프렌드로 선언 friend bool RectManager::equals(Rect r, Rect s); }; class Rect { // RectManager 클래스의 모든 멤버 함수를 Rect 클래스에 프랜드로 선언 friend RectManager; };
  • friend 키워드로 클래스 내에 선언된 함수
    • 클래스의 모든 멤버에 접근할 수 있는 권한 부여
    • 프렌드 함수라고 부름
  • 프렌드 선언의 필요성
    • 클래스의 멤버로 선언하기에는 무리가 있고, 클래스의 모든 멤버를 자유롭게 접근할 수 있는 일부 함수 작성시
    • 클래스 멤버로는 적합하지 않지만, private protected 멤버를 접근해야하는 특별한 경우
  • 프렌드 함수 개수에 제한 없음
#include <iostream>
using namespace std;

class Rect;  // Rect 클래스가 선언되기 전에 먼저 참조되는 컴파일 오류를 막기 위한 선언문
bool equals(Rect r, Rect s);

class Rect {
	int width, int height;
public:
	Rect(int width, int height) {
		this->width = width;
		this->height = height;
	}
	friend bool equals(Rect r, Rect s);  // Recet의 멤버함수가 아님
};

// Rect r과 s 복사 생성
bool equals(Rect r, Rect s) {
	// 객체 외부에서 private 멤버에 접근한다.
	// 이게 가능한 이유는 Rect 에서 equals를 friend 삼았기 떄문
	if (r.width == s.width && r.height == s.height) return ture;
	else return false;
}

int main() {
	Rect a(3,4), b(4,5);
	if (equals(a,b)) cout << "equal" << endl;
	else cout << "not equal" << endl;
}
	

다른 클래스의 멤버 함수를 프렌드로 선언

#include <iostream>
using namespace std;

// Rect 클래스가 선언되기 전어 먼저 참조되는 컴파일 오류를 막기 위한 선언문
class Rect;  

class RectManager {
public:
	bool equals(Rect r, Rect s);
};

class Rect {
	int width, height;
public:
	Rect(int width, int height) { this->width = width; this-> height = height; }
	// 어느 클래스에서 왔는지 지정
	friend bool RectManager::equals(Rect r, Rect s);
};

bool RectManager::equals(Rect r, Rect s) {
	if (r.width == s.widht && r.height == s.height) return true;
	else return false;
}

int main() {
	Rect a(3,4), b(3,4);
	RectManger man;

	if (man,equals(a,b)) count << "equal" << endl;
	else cout << "not equal" << endls;
}

다른 클래스 전체를 프렌드로 선언

#include <iostream>
using namespace std;

class Rect;  // Rect 클래스가 선언되기 전어 먼저 참조되는 컴파일 오류를 막기 위한 선언문

class RectManager {
public:
	bool equals(Rect r, Rect s);
	void copy(Rect& dest, Rect& src);
};

class Rect {
	int width, height;
public:
	Rect(int width, int height) { this->width = width; this-> height = height; }
	// 어느 클래스에서 왔는지 지정
	friend RectManager;
};

bool RectManager::equals(Rect r, Rect s) {
	if (r.width == s.widht && r.height == s.height) return true;
	else return false;
}

void RectManger::copy(Rect& dest, Rect& src) {
	dest.width = src.width;
	dest.height = src.height;
}

연산자 중복 operator overloading

  • 본래부터 있던 연산자만! 새로운 의미 정의
  • 일종의 다형성 polymorphism
  • 높은 프로그램 가독성
  • 피연산자 타입이 다른 새로운 연산 정의
  • 연산자는 함수 형태로 구현
  • 반드시 클래스와 관계를 가짐
  • 피연산자의 개수를 바꿀 수 없음
  • 연산의 우선 순위 변경 안됨
  • 모든 연산자가 중복 가능하지 않음 - . .* :: 범위지정연산자 ? : 3항연산자 등등…

연산자 함수 구현 방법 2가지

리턴타입 operator연산자 (매개변수리스트);
  1. 클래스의 멤버 함수로 구현
  2. Color operator+ (Color op1, Color op2); bool operator== (Color op1, Color op2); class Color { friend Color operator+ (Color op1, Color op2); friend bool operator== (Color op1, Color op2);
  3. 외부 함수로 구현하고 클래스에 프렌드 함수로 선언
  4. class Color { // 나와 비교하려는 객체를 받아서 나에게 더한다. // 왼쪽 피연산자는 객체 자신이므로 매개 변수에 전달되지 않는다. // 오른쪽 피연산자가 op2에 전달 Color operator+ (Color op2); bool operator== (Color op2); };

+ 연산자 중복

// c = a + b; -> C = a.operator+(b); b가 인수로서 전달
class Power {
	int kick;
	int punch;
public:
	Power(int kick=0, int punch=0) {
		this->kick = kick; this->punch = punch;
	}
	// 클래스의 멤버 함수로 구현하는 거니까 friend를 쓰면 안된다.
	// Power& op2로 해도 된다~
	Power operator+ (Power op2);
	Power operator+ (int op2);
};

// 묵시적으로 op2 복사
Power Power::operator+(Power op2) {
	// 더해질 새로운 객체
	Power tmp;
	// 나의 kick에 op2의 kick을 더함
	tmp.kick = this->kick + op2.kick;
	tmp.punch = this->punch + op2.punch;
	return tmp;
}

Power Power::operator+(int op2) {
	Power tmp;
	tmp.kick = kick + op2;
	tmp.punch = punch + op2;
	return tmp;
}

== 연산자 중복

//  a == b;  ->  a.operator==(b);
#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);
};

bool Power::operator==(Power op2) {
	if (kick == op2.kick&& punch == op2.punch) return true;
	else return false;
}

+= 연산자 중복 (참조 반환 주의!!!)

// c = a += b;   ->  c = a.+=(b);
// 업데이트된 a를 c에 대입
class Power {
	int kick;
	int punch;
public:
	Power& operator+= (Power op2);
}

// 자기 자신 참조를 반환
Power& Power::operator+=(Power op2) {
	// this-> 생략됨
	kick = kick + op2.kick;
	punch = punch + op2.punch;
	return *this;
}

단항 연산자 중복

단항 연산자

  • 피연산자가 하나 뿐인 연산자
  • 전위 연산자, 후위 연산자

멤버 함수로 구현

전위 ++ 연산자 중복 (시험문제 나옴)

// ++a  -> a.++()
class Power {
	int kick;
	int punch;
public:
	Power(int kick=0, int punch=0) {
		this->kick = kick; this->punch = punch;
	}
	void show();
	Power& operator++();
	bool operator!();
};

void Power::show() {
	cout << "kick=" << kick << ',' << "punch=" << punch << endl;
}

Power& Power::operator++() {  // 매개변수
	// 멤버변수와 매개 변수 이름이 같을때 this를 씀. 여기서는 뭐 굳이?
	kick++;
	punch++;
  // 갱신된 객체 자신의 참조 리턴
	return *this;
}

bool Power::operator!() {
	if (kick == 0 && punch == 0) return true;
	else return false;
}

후위 ++ 연산자 중복 (시험문제 나옴)

// ++a  -> a.++(임의의 정수)
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);  
	bool operator!();
};

void Power::show() {
	cout << "kick=" << kick << ',' << "punch=" << punch << endl;
}

// 주의! 연산자 함수는 디폴트 매개변수를 가질 수 없다.
// 컴파일러가 자동으로 x에 0 대입
Power Power::operator++(int x) {
	// 증가 이전의 객체 상태를 저장
	Power tmp = *this;
	kick++;
	punch++;
	// 증가 이전 객체 상태 리턴
	return tmp;
}

bool Power::operator!() {
	if (kick == 0 && punch == 0) return true;
	else return false;
}

프렌드로 구현

// ++a  -> a.++(임의의 정수)
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);  // 매개 변수 있음!! 전위 연사자와 구별하려고
	bool operator!();
};

void Power::show() {
	cout << "kick=" << kick << ',' << "punch=" << punch << endl;
}

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

bool Power::operator!() {
	if (kick == 0 && punch == 0) return true;
	else return false;
}

2 + a 덧셈을 위한 + 연산자 함수 작성

// b = 2 + a;  ->  b = +(2,a);
// 2는 클래스 아닌 기본타입이라 b = 2.+(a);로 변환이 불가능하다
class Power {
	int kick;
	int punch;
public:
	Power(int kick=0, int punch=0) {
		this->kick = kick; this->punch = punch;
	}
	void show();
	friend Power operator+(int op1, Power op2);
};

Power operator++(int op1, Power op2) {
	Power tmp;
	// private 속성인 kick, punch를 접근하기 위해 연산자 함수를 friend로 선언해야함
	tmp.kick = op1 + op2.kick;
	tmp.punch = op1 + op2.punch;
	return tmp;
}

+연산자를 외부 프렌드 함수로 구현

// c = a + b;  -> c = +(a,b);
class Power {
	int kick;
	int punch;
public:
	Power(int kick=0, int punch=0) {
		this->kick = kick; this->punch = punch;
	}
	void show();
	friend Power operator+(Power op1, Power op2);
};

Power operator+(Power op1, Power op2) {
	Power tmp;
	tmp.kick = op1.kick + op2.kick;
	tmp.punch = op1.punch + op2.punch;
	return tmp;
}

참조를 리턴하는 << 연산자 작성

  • 연속적으로 더하는 연산을 하기 위해 원본(*this)에 대한 참조를 반환해야한다.
Power& operator<< (int n);

Power& Power::operator<< (int n) {
	kick += n;
	punch += n;
	return *this;
}