관리 메뉴

기억을 위한 기록들

[CPP-effective] 4-6 멤버 함수보다는 비멤버 비프렌드 함수와 더 가까워지자. 본문

C & CPP/Effective C++

[CPP-effective] 4-6 멤버 함수보다는 비멤버 비프렌드 함수와 더 가까워지자.

에드윈H 2021. 4. 15. 18:41

어떤 캐릭터 클래스를 만들다가 캐릭터가 죽어서 다시 처음부터 진행되기 위해 상태를 초기화해주는 함수들이 있다고 가정합시다.

 

class Character
{
public:
	//..
	void SetMaxHP(); //최대체력 만들어주기
	void ClearStat(); //능력치 초기화
	void ClearInventory(); //인벤토리 비우기..등
	//..	
};

이렇게 3개면 기획상의 추가로 점점 많이지게 되면, 한 번에 호출하고 싶게 된다.

class Character
{
public:
	//..
	void SetMaxHP(); 
	void ClearStat();
	void ClearInventory(); 
	//..	

	void ClearEverything() //한번에 호출해주는 함수 추가
	{
		SetMaxHP();
		ClearStat();
		ClearInventory();
		//..더 추가될수도?
	}
};

 

이렇게 편하게 ClearEverything 함수를 추가해주었다. 물론 이렇게 멤버 함수 말고 비멤버 함수로도 가능하긴 하다.

 

void ClearEveryThing2(Character& ref) //미멤버 함수 ClearEveryThing2
{
	ref.SetMaxHP();
	ref.ClearStat();
	ref.ClearInventory();
}

 

두 가지 중 어느 쪽이 더 괜찮을까?

 

 

어떻게 보면 첫 번째 방법인 멤버 함수가 나아 보인다. 하지만 틀렸다. 객체 지향의 법칙은 할 수 있는 만큼 데이터를 캡슐화해야 하는데 멤버 버전인 ClearEverything 함수는 캡슐화가 ClearEverything2보다 못하다.

(멤버 함수 ClearEverything는 클래스의 private 멤버 데이터, 멤버 함수도 호출이 가능해진다.)

 

그거 말고도 비멤버 함수 ClearEverything2는 패키지 유연성이 높아지는 장점도 있고, 추가적으로 컴파일 의존도도 낮추고 Character 클래스의 확장성도 높일 수 있다.

 

캡슐화 측면에서 

ClearEverything2(비멤버 함수)가 ClearEverything(멤버 함수) 보다 높다. 그런데 주의해야 할 점이 있는데,

 

1. 비멤버 비프렌드 함수에만 적용된다.

프렌드 함수는 private 멤버에 대한 접근이 해당 클래스의 멤버 함수가 가진 권한과 같아지게에, 캡슐화 영향이 있다.

 

2. "함수는 어떤 클래스의 비멤버가 되어야한다" 라는 주장이 "그 함수는 다른 클래스의 멤버가 될 수 없다"라는 건 아니다.

예를 들어, ClearEverything2(비멤버 함수)를 다른 유틸리티 클래스 같은 데의 정적 멤버 함수로 만들어도 된다는 것이다.

Character 클래스의 멤버가 아니기만 하면 된다.

 

 

좀 더 C++로 자연스러운 방법은 같은 namespace안에 두는 것이다.

namespace EnemyCharacter
{
	class Character
	{
	public:
		//..
		void SetMaxHP();
		void ClearStat();
		void ClearInventory();
		//..	
	};


	void ClearEveryThing2(Character& ref) //미멤버 함수 ClearEveryThing2
	{
		ref.SetMaxHP();
		ref.ClearStat();
		ref.ClearInventory();
	}
}

 

사실 이런 ClearEveryThing2 함수는 편의상 준비한 함수들이긴 하다. 멤버도 아니고 프렌드도 아니기에 ClearEveryThing2 함수가 없다고 해서 SetMaxHP(), ClearStat(), ClearInventory() 함수를 못 부르는 것이 아니기 때문이다.

 

그리고 클래스와 달리 네임스페이스는 헤더파일 여러 개로 쪼개어 나누어져도 된다는 것이다.

namespace EnemyCharacter
{
	class Character
	{
	public:
		//..
		void SetMaxHP();
		void ClearStat();
		void ClearInventory();
		//..	
	};
}

namespace EnemyCharacter
{
	void ClearEveryThing2(Character& ref) 
	{
		ref.SetMaxHP();
		ref.ClearStat();
		ref.ClearInventory();
	}
}

 

편의 함수 전체를 여러 개의 헤더 파일(그러나 하나의 같은 네임스페이스)에 나누어 놓으면 편의 함수 집합의 확장도 쉬워진다.

 

해당 네임스페이스에 비멤버 비프렌드 함수를 원하는 만큼 추가해 주기만 하면 그게 바로 확장이다.

 

 

정리

* 멤버 함수보다는 미멤버 비프랜드 함수를 자주 쓰도록 하자. 캡슐화 정도가 높아지고 패키징 유연성도 커지며, 기능적인 확장성도 늘어난다.