연재 완료/C++ Lang 이론

10-1. 프렌드 키워드

라이피 (Lypi) 2018. 7. 3. 01:59
반응형

PART 2. 객체 지향 프로그래밍

  - chapter 10. 프렌드와 연산자 중복


1. friend 키워드

  - 기본적으로 private로 선언된 멤버들은 외부에서 접근할 수 없다. 심지어는 상속받은 클래스도 부모의 private 멤버들에는 접근할 수 없다.

  - friend 키워드는 선언한 쪽의 private 멤버를 외부에서 접근할 수 있도록 허용한다는 의미이다.

  - friend 지정은 단방향으로 명시적으로 지정한 대상에만 적용된다.

  - friend 지정은 전이되지 않으며 friend의 friend는 인정되지 않는다.

  - friend 지정은 상속되지 않는다.

  - friend 지정은 클래스 내에서만 할 수 있고, 다른 클래스, 다른 클래스의 멤버 함수, 전역 함수에 할 수 있다.


Header.h

 #pragma once
#include <iostream>
#include <string>
#include <ctime>
#include <tchar.h>


using std::cout;
using std::cin;
using std::endl;
using std::string;

Card_one.h

 #pragma once
#include "Header.h"

//전방선언. 이런 클래스가 나올거라고 알려주는 의미.
class Card_deck;

const char SPADE[3] = "♠";
const char DIAMOND[3] = "◆";
const char HEART[3] = "♥";
const char CLUB[3] = "♣";

class Card_one
{
	int     pattern;
	int     number;
	bool    open;

	bool    showData();
	bool    setOpen(bool onoff);
	bool    getOpen();

	//생성자도 private이므로 일반적인 방법으로는 객체 생성이 안되는 클래스.
	Card_one();
	Card_one(int p, int n);
	~Card_one();

	//카드 덱 클래스를 friend 지정.
	friend class Card_deck;

};

Card_one.cpp

 #include "Card_one.h"

bool Card_one::showData()
{
	if (open == true) {
		switch (pattern) {
			case 0: cout << SPADE;   break;
			case 1: cout << DIAMOND; break;
			case 2: cout << HEART;   break;
			case 3: cout << CLUB;    break;
		}
		switch (number) {
			 case  1: cout << " A"; break;
			 case 11: cout << " J"; break;
			 case 12: cout << " Q"; break;
			 case 13: cout << " K"; break;
			 default: cout << " " << number; break;	}
		cout << endl;
		return true;
	}
	else {
		cout << "???" << endl;
		return false;
	}
}

bool Card_one::setOpen(bool onoff)
{
	open = onoff;
	return open;
}

bool Card_one::getOpen()
{
	return open;
}

Card_one::Card_one()
{
	pattern = 0;
	number = 0;
	open = false;
}

Card_one::Card_one(int p, int n)
{
	pattern = p;
	number = n;
	open = false;
}

Card_one::~Card_one()
{
}

Card_deck.h

 #pragma once

//friend 지정된 클래스에서는 해당 클래스에 대한 완전한 정보가 필요하다.
#include "Card_one.h"

class peep_deck
{
public:
	void peep_ok(Card_deck& D);
	//void peep_no(Card_deck& D);

	peep_deck() {}
	~peep_deck() {}
};

class Card_deck
{
private:
	Card_one Deck[52];
	int currentCnt;

public:
	void swapCard(Card_one& srcCard, Card_one& dstCard);
	void Shuffle(int cnt);
	void cardOpen();

	Card_deck();
	virtual ~Card_deck();

	//friend 전역 함수 선언. 
	//CntChoice 함수는 전역 함수가 되므로 Card_deck클래스의 멤버가 아니게 된다.
	friend void CntChoice(Card_deck&p, int n)
	{
		p.currentCnt = n;
	}

	//peep_deck 클래스의 peep_ok()메소드만 friend 지정
	//특정 클래스의 멤버를 friend로 선언하려면 그 클래스의 선언이 이 클래스의 앞에 있고, 멤버 함수의 몸체는 뒤에 있어야 한다. 
	friend void peep_deck::peep_ok(Card_deck& D);

};

Card_deck.cpp

 #include "Card_deck.h"


Card_deck::Card_deck()
{
	//카드 세팅
	for (int iCnt = 0; iCnt < 52; iCnt++) {
		switch (iCnt / 13) {
		case 0: Deck[iCnt] = Card_one(0, iCnt % 13 + 1); break;
		case 1: Deck[iCnt] = Card_one(1, iCnt % 13 + 1); break;
		case 2: Deck[iCnt] = Card_one(2, iCnt % 13 + 1); break;
		case 3: Deck[iCnt] = Card_one(3, iCnt % 13 + 1); break;
		}
		//특수문자는 char형의 표현범위를 벗어나기 때문에 경고가 발생한다.
		//C언어에서 특수문자나 다국어 처리는 상당히 까다로운 부분.
		//일단 여기서는 char를 wchar_t로 바꾸는 것으로 해결했다.
		//참고 : 멀티바이트, 유니코드, ICU(International Components for Unicode)
	}

	currentCnt = 0;
}

void Card_deck::swapCard(Card_one& srcCard, Card_one& dstCard)
{
	Card_one temp = srcCard;
	srcCard = dstCard;
	dstCard = temp;
}

void Card_deck::Shuffle(int cnt)
{
	srand((unsigned)time(NULL));

	cout << "덱을 " << cnt << "번 섞습니다." << endl;
	
	for (int iCnt = 0; iCnt < cnt; iCnt++) {
		int src = rand() % 52;
		int dst = rand() % 52;

		swapCard(Deck[src], Deck[dst]);
	}

	cout << "덱을 섞었습니다." << endl;
}

void Card_deck::cardOpen()
{
	Deck[currentCnt].open = true;
	Deck[currentCnt].showData();
	currentCnt++;
}


Card_deck::~Card_deck()
{
}



void  peep_deck::peep_ok(Card_deck& D)
{
	D.cardOpen();
	D.currentCnt--;
}

//friend 선언 안한 함수에서는 접근 불가.
//void peep_deck::peep_no(Card_deck& D)
//{
//	D.cardOpen();
//	D.currentCnt--;
//}

main.cpp

 #include "Card_deck.h"

int main()
{
	Card_deck deck;

	deck.Shuffle(100);
	deck.cardOpen();

	//전역 함수가 되었으므로 볼 수 없다.
	//deck.CntChoice(deck, 42);

	CntChoice(deck,42);

	peep_deck pd;
	pd.peep_ok(deck);
}


반응형