공부/C++

9. friend와 연산자 중복

bokob 2024. 5. 16. 19:31

9.1 C++에서의 friend

C++ friend

friend 함수

  • 클래스의 멤버 함수가 아닌 외부 함수
    • 전역 함수, 다른 클래스의 멤버 함수
  • friend 키워드로 클래스 내에 선언된 함수: friend 함수라고 부름
    • 클래스 정의 안쪽에 friend 키워드를 쓰고 함수의 선언을 적어 줌.
    • 클래스의 모든 멤버를 접근할 수 있는 권한 부여
  • friend 선언의 필요성
    • 클래스의 멤버로 선언하기에는 무리가 있고, 클래스의 모든 멤버를 자유롭게 접근할 수 있는 일부 외부 함수 작성 시
항목 실제 세계에서의 친구 프렌드 함수
존재 가족 X, 외부인 클래스 외부에 작성된 함수, 멤버가 아님
자격 친하면 가족 구성원으로 인정 받을 수 있음,  자기거 뭐 만져도 되는 수준 ex) 하이킥 객식구 클래스의 멤버 자격 부여, 클래스의 모든 멤버에 대해 접근 가능
선언 친구라고 소개 클래스 내에 friend 키워드로 선언
개수 친구의 명수에 제한 없음 프렌드 함수 개수에 제한 없음

 

friend로 초대하는 3 가지 유형

friend 함수가 되는 3가지

  • 전역 함수: 클래스 외부에 선언된 전역 함수
  • 다른 클래스의 멤버 함수: 다른 클래스의 특정 멤버 함수
  • 다른 클래스 전체: 다른 클래스의 모든 멤버 함수

 

friend 선언 3종류 예시

1. 외부 함수 equals()를 Rect 클래스에 friend로 선언

class Rect{
    // ...
    friend bool equals(Rect r, Rect s);
};

2. RectManager 클래스의 equals() 멤버 함수를 Rect 클래스에 friend로 선언

class Rect{
    // ...
    friend bool RectManager::equals(Rect r, Rect s);
};

3. RectManager 클래스의 모든 멤버 함수(클래스 자체)를 Rect 클래스에 friend로 선언

class rect{
    // ...
    friend RectManager;
};

 

전역함수를 friend로 선언

#include <iostream>
using namespace std;

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

class Rect { // Rect 클래스 선언
    int width, height;
public:
    Rect(int width, int height) { this->width = width; this->height = height; }
    friend bool equals(Rect r, Rect s); // equals() 함수를 프렌드로 선언
};

bool equals(Rect r, Rect s) { // 외부 함수
    // friend이기 때문에 private 변수 접근 가능
    if (r.width == s.width && r.height == s.height) return true;
    else return false;
}

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

/* 결과
not equal
*/

 

다른 클래스의 멤버 함수를 friend로 선언

#include <iostream>
using namespace std;

class Rect;

class RectManager { // RectManager 클래스 선언
public:
    bool equals(Rect r, Rect s);
};

class Rect { // 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) {
    // RectManager 클래스의 equals() 멤버를 프렌드로 선언
    if (r.width == s.width && r.height == s.height) return true;
    else return false;
}

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

/* 결과
equal
*/

 

다른 클래스 전체를 friend로 선언

#include <iostream>
using namespace std;

class Rect;
class RectManager { // RectManager 클래스 선언

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

class Rect { // Rect 클래스 선언
    int width, height;
public:
    Rect(int width, int height) { this->width = width; this->height = height; }
    friend RectManager; // RectManager 클래스를 프렌드로 선언
};
bool RectManager::equals(Rect r, Rect s) { // r과 s가 같으면 true 리턴
    if (r.width == s.width && r.height == s.height) return true;
    else return false;
}
void RectManager::copy(Rect& dest, Rect& src) { // src를 dest에 복사
    dest.width = src.width; dest.height = src.height;
}

int main() {
    Rect a(3, 4), b(5, 6);
    RectManager man;
    man.copy(b, a); // a를 b에 복사한다.
    if (man.equals(a, b)) cout << "equal" << endl;
    else cout << "not equal" << endl;
}

/* 결과
equal
*/

 

9.2 연산자 중복

연산자 중복

일상 생활에서의 기호 사용

  • + 기호를 숫자와 물체에 적용, 중복 사용
  • + 기호를 숫자가 아닌 곳에도 사용 ex) 빨강 + 파랑 = 보라
  • 다형성

 

C++ 언어에서도 연산자 중복 가능

  • C++ 언어에 본래부터 있던 연산자에 새로운 의미 정의
  • 높은 프로그램 가독성

 

연산자 중복의 사례: + 연산자에 대해

정수 더하기

int a=2, b=3, c;
c = a + b; // + 결과 5. 정수가 피연산자일 때 2와 3을 더함

문자열 합치기

string a = "C", c;
c = a + "++"; // + 결과 "C++". 문자열이 피연산자일 때 두 개의 문자열 합치기

색 섞기

Color a(BLUE), b(RED), c;
c = a + b; // c = VIOLET. a, b의 두 색을 섞은 새로운 Color 객체 c

배열 합치기

SortedArray a(2,5,9), b(3,7,10), c;
c = a + b; // c = {2,3,5,7,9,10}. 정렬된 두 배열을 결합한 새로운 배열 생성

 

연산자 중복의 특징

연산자 중복의 특징

  • C++에 본래 있는 연산자만 중복 가능 ex) 3%%9 // 컴파일 오류
  • 피연산자 타입이 다른 새로운 연산 정의
  • 연산자는 함수 형태로 구현 - 연산자 함수(operator function)
  • 반드시 클래스와 관계를 가짐
  • 피연산자의 개수를 바꿀 수 없음
  • 연산의 우선 순위 변경 안됨
  • 모든 연산자가 중복 가능하지 않음
  • 중복 가능한 연산자
+ - * / % ^ &
| ~ ! = < > +=
-= *= /= %= ^= &= |=
<< >> >>= <<= == != >=
<= && || ++ -- ->*(멤버에 대한 포인터) ,
-> [ ] ( ) new delete new[] delete[]
  • 중복 불가능한 연산자
. (멤버에 대한 선택) .* (멤버에 대한 포인터) ::(범위지정 연산자) ?: (3항 연산자)

 

연산자 함수

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

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

2. 외부 함수로 구현하고 클래스에 friend 함수로 선언

연산자 함수 형식

리턴타입 operator연산자(매개변수리스트);

 

+와 == 연산자 작성 사례

 

멤버 함수로 이항 연산자 중복 구현1: + 연산자 중복

class Power{
    int kick;
    int punch;
public:
    Power(int kick=0, int punch=0){
        this->kick = kick;
        this->punch = punch;
    }
};

 

2개의 Power 객체를 더하는 + 연산자 작성

#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+(Power op2); // + 연산자 함수 선언
};

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

Power Power::operator+(Power op2) {
    Power tmp; // 임시 객체 생성
    tmp.kick = this->kick + op2.kick; // kick 더하기
    tmp.punch = this->punch + op2.punch; // punch 더하기
    return tmp; // 더한 결과 리턴
}

int main() {
    Power a(3, 5), b(4, 6), c;
    c = a + b; // 파워 객체 + 연산
    a.show();
    b.show();
    c.show();
}

/* 결과
kick=3, punch=5
kick=4, punch=6
kick=7, punch=11
*/

 

멤버 함수로 이항 연산자 중복 구현2: == 연산자 중복

 

2개의 Power 객체를 비교하는 == 연산자 작성

#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); // == 연산자 함수 선언
};

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

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

int main() {
    Power a(3, 5), b(3, 5); // 2 개의 동일한 파워 객체 생성
    a.show();
    b.show();

	// operator==() 멤버 함수 호출
    if (a == b) cout << "두 파워가 같다." << endl;
    else cout << "두 파워가 같지 않다." << endl;
}

/* 결과
kick=3,punch=5
kick=3,punch=5
두 파워가 같다.
*/

 

멤버 함수로 이항 연산자 중복 구현3: += 연산자 중복

 

두 Power 객체를 더하는 += 연산자 작성

#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+= (Power op2); // += 연산자 함수 선언
};

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

Power Power::operator+=(Power op2) {
    kick = kick + op2.kick; // kick 더하기
    punch = punch + op2.punch; // punch 더하기
    return *this; // 합한 결과 리턴
}

int main() {
    Power a(3, 5), b(4, 6), c;
    a.show();
    b.show();
    
    // operator+=() 멤버 함수 호출
    c = a += b; // 파워 객체 더하기
    a.show();
    c.show();
}

/* 결과
kick=3,punch=5
kick=4,punch=6
kick=7,punch=11
kick=7,punch=11
*/

 

멤버 함수로 단항 연산자 중복 구현

단항 연산자

  • 피연산자가 하나 뿐인 연산자
    • 연산자 중복 방식은 이항 연산자의 경우와 거의 유사함
  • 단항 연산자 종류
    • 전위 연산자(prefix operator) ex) !op, ~op, ++op, --op
    • 후위 연산자(postfix operator) ex) op++, op--

 

멤버 함수로 단항 연산자 중복 구현1: 전위 ++ 연산자 중복

전위 ++ 연산자 작성

#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; // 변경된 객체 자신(객체 a) 리턴
}

int main() {
    Power a(3, 5), b;
    a.show();
    b.show();
    // operator++() 함수 호출
    b = ++a; // 전위 ++ 연산자 사용
    a.show();
    b.show();
}

/* 결과
kick=3,punch=5
kick=0,punch=0
kick=4,punch=6
kick=4,punch=6
*/

 

멤버 함수로 단항 연산자 중복 구현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++ (int x); // 후위 ++ 연산자 함수 선언
};

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

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

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

/* 결과
kick=3,punch=5
kick=0,punch=0
kick=4,punch=6
kick=3,punch=5
*/

 

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

 

 2 + a를 위한 + 연산자 함수를 friend로 작성

#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();
    friend Power operator+(int op1, Power op2); // 프렌드 선언
};

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

// + 연산자 함수를 외부 함수로 구현
Power operator+(int op1, Power op2) {
    Power tmp; // 임시 객체 생성
    
    // private 변수에 접근하기 위해, 연산자 함수를 friend로 선언해야 함
    tmp.kick = op1 + op2.kick; // kick 더하기
    tmp.punch = op1 + op2.punch; // punch 더하기 =
    return tmp; // 임시 객체 리턴
}

int main() {
    Power a(3, 5), b;
    a.show();
    b.show();
    b = 2 + a; // 파워 객체 더하기 연산
    a.show();
    b.show();
}

/* 결과
kick=3,punch=5
kick=0,punch=0
kick=3,punch=5
kick=5,punch=7
*/

 

+ 연산자를 외부 friend 함수로 구현

 

a+b를 위한 연산자 함수를 friend로 작성

#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();
    friend Power operator+(Power op1, Power op2); // 프렌드 선언
};

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

Power operator+(Power op1, Power op2) {
    Power tmp; // 임시 객체 생성
    tmp.kick = op1.kick + op2.kick; // kick 더하기
    tmp.punch = op1.punch + op2.punch; // punch 더하기
    return tmp; // 임시 객체 리턴
}

int main() {
    Power a(3, 5), b(4, 6), c;
    
    // operator+(a, b) 함수 호출
    c = a + b; // Power 객체 + 연산
    a.show();
    b.show();
    c.show();
}

/* 결과
kick=3,punch=5
kick=4,punch=6
kick=7,punch=11
*/

 

단항 연산자 ++를 friend로 작성하기

 

++ 연산자를 friend로 작성한 예

#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();
	friend Power operator++(Power& op); // 전위 ++ 연산자 함수 프렌드 선언
	friend Power operator++(Power& op, int x); // 후위 ++ 연산자 함수 프렌드 선언
};
void Power::show() {
	cout << "kick=" << kick << ',' << "punch=" << punch << endl;
}

Power operator++(Power& op) { // 전위 ++ 연산자 함수 구현
	op.kick++;
	op.punch++;
	return op; // 연산 결과 리턴
}
Power operator++(Power& op, int x) { // 후위 ++ 연산자 함수 구현
	Power tmp = op; // 변경하기 전의 op 상태 저장
	op.kick++;
	op.punch++;
	return tmp; // 변경 이전의 op 리턴
}
int main() {
	Power a(3, 5), b;
	b = ++a; // 전위 ++ 연산자
	a.show(); b.show();
	b = a++; // 후위 ++ 연산자
	a.show(); b.show();
}

/* 결과
kick=4,punch=6
kick=4,punch=6
kick=5,punch=7
kick=4,punch=6
*/

 

'공부 > C++' 카테고리의 다른 글

11. 가상함수와 추상클래스  (0) 2024.06.02
10. 상속  (0) 2024.05.27
8. 함수와 참조, 복사생성자  (1) 2024.05.14
7. 여러가지 객체의 생성방법  (0) 2024.05.14
6. 접근지정자, const 객체, static 멤버  (1) 2024.04.18