Search

Effective C++ 23: 멤버 함수보다는 비멤버 비프렌드 함수와 더 가까워지자

개요

C++에서 클래스를 설계할 때, 우리는 종종 '데이터와 그 데이터를 다루는 함수는 한 곳에 있어야 한다'는 객체 지향 원칙을 따릅니다. 그러나 이번 항목에서 Effective C++은 이러한 접근이 항상 최선은 아닐 수 있다고 제안합니다. 이번 글에서는 왜 때로 멤버 함수보다 비멤버 비프렌드 함수를 선호해야 하는지, 그리고 이것이 어떻게 더 나은 캡슐화와 유연성을 제공하는지 살펴보겠습니다.

본문

멤버 함수 vs 비멤버 비프렌드 함수

먼저 간단한 WebBrowser 클래스 예제를 통해 멤버 함수와 비멤버 함수의 차이를 살펴보겠습니다.
class WebBrowser { public: void clearCache(); void clearHistory(); void removeCookies(); // 멤버 함수 버전 void clearEverything() { clearCache(); clearHistory(); removeCookies(); } }; // 비멤버 함수 버전 void clearBrowser(WebBrowser& wb) { wb.clearCache(); wb.clearHistory(); wb.removeCookies(); }
C++
복사
얼핏 보면 clearEverything()을 멤버 함수로 구현하는 것이 더 자연스러워 보일 수 있습니다. 그러나 이번 항목은 비멤버 비프렌드 함수인 clearBrowser()가 더 나은 선택이 될 수 있다고 주장합니다.

캡슐화와 유연성

놀랍게도, 비멤버 비프렌드 함수는 멤버 함수보다 더 나은 캡슐화를 제공합니다. 이는 비멤버 비프렌드 함수가 클래스의 private 멤버에 접근할 수 없기 때문입니다. 즉, 클래스의 public 인터페이스만을 사용하여 기능을 구현해야 하므로, 클래스의 내부 구현에 대한 의존성이 줄어들게 됩니다.
또한, 비멤버 함수를 사용하면 관련 기능을 구성하는 데 있어 패키징 유연성이 높아집니다. 예를 들어, 웹 브라우저의 다양한 기능을 여러 개의 헤더 파일로 나눠 구현할 수 있습니다:
// webbrowser.h namespace WebBrowserStuff { class WebBrowser { ... }; // 핵심 관련 기능 } // webbrowserbookmarks.h namespace WebBrowserStuff { // 북마크 관련 기능 } // webbrowsercookies.h namespace WebBrowserStuff { // 쿠키 관련 기능 }
C++
복사
이렇게 구성하면 사용자는 필요한 기능만 포함하여 컴파일할 수 있으며, 이는 컴파일 의존성을 줄이고 빌드 시간을 단축시킬 수 있습니다.

네임스페이스의 활용

C++에서는 네임스페이스를 활용하여 비멤버 함수를 효과적으로 구조화할 수 있습니다:
namespace WebBrowserStuff { class WebBrowser { ... }; void clearBrowser(WebBrowser& wb); // 다른 관련 비멤버 함수들 }
C++
복사
이렇게 하면 관련 기능을 논리적으로 그룹화하면서도, 클래스의 캡슐화를 유지할 수 있습니다.

개인적 견해

이 원칙을 처음 접했을 때, 객체 지향의 기본을 거스르는 것 같아 혼란스러웠습니다. 그러나 깊이 생각해보니, 이는 더 큰 그림에서 코드의 유연성과 확장성을 고려하는 접근법이라는 것을 깨달았습니다. 단순히 "데이터와 함수를 함께 두어라"는 원칙을 맹목적으로 따르는 것이 아니라, 시스템 전체의 구조를 고려하는 것입니다.
실제 프로젝트에 이 원칙을 적용할 때는 기능의 범위, 확장성, 재사용성을 고려해야 합니다. 각 상황에 맞게 멤버 함수와 비멤버 비프렌드 함수 중 적절한 것을 선택함으로써, 더 유연하고 확장 가능한 코드를 작성할 수 있을 것입니다. 이는 C++뿐만 아니라 다른 객체 지향 언어를 사용할 때도 유용한 통찰을 제공할 것입니다.

결론

"멤버 함수보다는 비멤버 비프렌드 함수와 더 가까워지자"는 원칙은 더 나은 캡슐화, 유연성, 그리고 확장성을 제공합니다. 그러나 이는 모든 상황에 무조건적으로 적용할 수 있는 규칙이 아닙니다. 각 상황에 맞게 판단하여, 클래스의 책임과 전체 시스템의 구조를 고려한 선택이 필요합니다.