effective C++

데이터 멤버는 private 멤버로 선언해라. 이를 통해 제작자는 문법적으로 일관성 있는 데이터 접근 통로를 제공할 수 있고, 필요에 따라서는 세밀한 접근 제어도 가능하며, 클래스의 불변속성을 강화할 수 있을 뿐 아니라, 내부 구현의 융통성도 발휘할 수 있다. protected는 public보다 더 많이 보호받고 있는 것이 아니다. 데이터 멤버는 private 영역에 데이터 멤버는 private 영역에 선언하는 것이 좋다. 이에 따른 이점을 살펴보자. 문법적 일관성 데이터 멤버가 public이라면, 사용자 쪽에서 어떤 객체를 접근할 수 있는 유일한 수단은 멤버 함수이다. 어떤 클래스에 공개 인터페이스가 전부 함수라면 클래스 멤버에 접근하고 싶을 때 괄호를 붙여야 하는지 여부를 일치시키기 쉽다. 함수를 사..
지역 스택 객체에 대한 포인터나 참조자를 반환하는 일, 혹은 힙에 할당된 객체에 대한 참조자를 반환하는 일, 지역 정적 객체에 대한 포인터나 참조자를 반환하는 일은 객체가 두 개 이상 필요해질 가능성이 있다면 절대 하지 마라. 지역 정적 객체에 대한 참조자를 반환하는 것은 괜찮을 수 있다(단일 스레드 한정) 함수에서 객체에 대한 참조자 반환 금지 값에 의한 전달이 효율적으로 문제가 된다는 사실 때문에 모든 코드를 참조에 의한 전달을 시도하려 할 수 있다. 하지만 문제가 생길 수 있기 때문에 조심해야 한다. 유리수를 나타내는 클래스가 있다고 가정하자. 유리수를 곱하는 opeartor*가 있다. class Rational { public: Rational(int numerator = 0, int denomi..
값에 의한 전달보다는 상수 객체 참조자에 의한 전달을 사용해라. 대체적으로 효율적이고 복사손실을 막을 수 있다. 기본제공 타입 및 STL반복자, 함수 객체 타입에는 값에 의한 전달이 더 적절하다. 값에 의한 전달보다는 상수객체 참조자에 의한 전달 C++은 함수로부터 객체를 전달받거나 함수에 객체를 전달할 때 값에 의한 전달 방식을 사용한다. 특별히 다른 방식을 지정하지 않는 한, 함수 매개변수는 실제 인자의 사본을 통해 초기화되며, 어떤 함수를 호출한 쪽은 그 함수가 반환한 값의 사본을 돌려받는다. 하지만, 값에 의한 전달은 비용이 비싸다. class Person { public: Person(); virtual ~Person(); ... private: std::string name; std::stri..
클래스 설계는 타입 설계와 같다. 새로운 타입을 정의하기 전에 몇 가지 고려사항을 점검해 보자. 클래스 설계는 타입 설계 여느 객체 지향 프로그래밍 언어처럼, C++에서 새로운 클래스를 정의한다는 것은 새로운 타입을 정의하는 것과 같다. C++ 개발을 한다는 것은 타입을 새로 만드는 일과 같으며 함수와 연산자를 오버로드하고, 메모리 할당 및 해제를 제어하며, 객체 초기화 및 종료처리를 정의하는 작업 모두 관리해 주어야 한다. 좋은 클래스를 설계하는 것은 꽤 어렵다. 문법이 자연스럽고, 의미구조가 직관적이며, 효율적인 구현이 가능해야 한다. 성능 또한 무시할 수 없는 부분이다. 다음은 좋은 클래스를 설계하기 위해 고려해 볼 사항 몇 가지이다. 새로 정의한 타입의 객체 생성 및 소멸은 어떻게 이루어져야 하는..
좋은 인터페이스는 제대로 쓰기엔 쉽고 틀리게 쓰기엔 어렵다. 인터페이스를 만들 때는 이 특성을 지닐 수 있도록 고민해야 한다. 사용자의 실수를 방지하는 방법으로는 새로운 타입 만들기, 타입에 대한 연산 제한, 객체의 값에 대해 제약 걸기, 자원 관리 작업을 사용자 책임으로 놓지 않기가 있다. 인터페이스의 올바른 사용을 이끄는 방법으로는 인터페이스 사이의 일관성 잡아주기, 기본 제공 타입과의 동작 호환성 유지하기가 있다. tr1::shared_ptr은 사용자 정의 삭제자를 지원한다. 이 특징 때문에 tr1::shared_ptr은 교차 DLL 문제를 막아 주며, 뮤텍스 등을 자동으로 잠금 해제하는 데 사용할 수 있다. 인터페이스는 제대로 쓰기엔 쉽게, 틀리게 쓰기엔 어렵게 C++에서 인터페이스의 활용도는 매..
new로 생성한 객체를 스마트 포인터에 넣는 코드는 한 문장으로 만들어라. 그렇지 않으면, 예외가 발생될 때 자원 누출 가능성이 생긴다. 새로 생성한 객체를 스마트 포인터로 관리할 때 주의점 자원 누출을 막기 위해 스마트 포인터를 사용하는 것은 좋은 방법이다. 하지만, 이 방법을 사용하더라도 자원 누출이 발생할 수 있다. int priority(); // 우선순위를 알려주는 함수 void processWidget(std::tr1::shread_ptr pw, int priority); // 우선순위에 따라 처리 우선순위에 따라 어떠한 작업을 하는 함수가 있다고 가정하자. processWidget(new Widget, priority()); 이렇게 함수를 사용할 수 없다. tr1::shared_ptr의 생성..
new 표현식에 []를 썼으면, 대응되는 delete 표현식에도 []를 써야 한다. 마찬가지로 new 표현식에 []를 안 썼으면, delete 표현식에도 []를 쓰면 안 된다. new, delete 사용 시 주의점 std::string *stringArray = new std::string[100]; ... delete stringArray; 문제가 없어 보이는 코드이지만, 미정의 동작을 보이게 된다. 최선의 경우에도 stringArray가 가리키는 100개의 string 객체 중 99개는 정상적으로 소멸되지 않을 가능성이 크다. 삭제되는 포인터가 객체 하나만 가리키는지 객체의 배열 가리키는지에 따라 delete가 소멸자를 몇 번 호출할지 결정한다. 단일 객체와 객체 배열에 대한 메모리 배치구조가 다르기..
실제 자원을 직접 접근해야 하는 기존 API들도 많기 때문에, RAII 클래스를 만들 때는 그 클래스가 관리하는 자원을 얻을 수 있는 방법을 제공해야 한다. 자원 접근은 명시적 변환 혹은 암시적 변환을 통해 가능하다. 안전성만 따지면 명시적 변환이 대체로 낫지만, 편의성을 보면 암시적 변환이 더 낫다. 자원 관리 클래스에서의 자원 접근 허용 자원 관리 클래스는 자원 누출을 막을 수 있는 보호막 역할을 한다. 하지만, 자원에 대한 직접적인 접근이 필요한 상황이 있다. 예를 들어, 스마트 포인터를 이용하여 자원 관리를 하는 경우 원래의 자원을 매개변수로 사용해야 하는 함수가 있을 때 스마트 포인터를 넘긴다면 에러가 발생한다. std::tr1::shared_ptr pInv(createInvestment());..
RAII 객체의 복사는 그 객체가 관리하는 자원의 복사 문제를 안고 가기 때문에, 그 자원을 어떻게 복사하느냐에 따라 RAII 객체의 복사 동작이 결정된다. RAII 클래스에 구현하는 일반적인 복사 동작은 복사를 금지하거나 참조 카운팅을 해주는 식으로 처리한다. 물론 다른 방법도 가능하다. 자원 관리 클래스의 복사 동작 힙에 생성되지 않는 자원은 스마트 포인터로 처리해 주기엔 일반적으로 맞지 않다. 예를 하나 들어보자. Mutex 타입의 뮤텍스 객체를 조작하는 C API를 사용 중이라고 가정해보자. void lock(Mutex *pm); // pm이 가리키는 뮤텍스에 잠금을 건다. void unlock(Mutex *pm) // pm이 가리키는 뮤텍스의 잠금을 푼다. 뮤텍스 잠금을 관리하는 클래스를 만드려..
자원 누출을 막기 위해, 생성자 안에서 자원을 획득하고 소멸자에서 그것을 해제하는 RAII 객체를 사용하자. 일반적으로 쓰이는 RAII 클래스는 tr1::shared_ptr, auto_ptr이다. tr1::shared_ptr이 복사 시의 동작이 직관적이기 때문에 더 좋다. auto_ptr은 원본 객체를 null로 만든다. 자원 관리 객체 자원이란, 사용을 일단 마치고 난 후엔 시스템에 돌려주어야 하는 모든 것을 일컫는다. 특히 동적 할당된 메모리가 있고 파일 서술자, 뮤텍스 잠금, GUI, 폰트, 브러시 등이 있다. 이러한 자원들을 관리하는데 효과적인 방법 중 하나는 자원 관리 객체를 사용하는 것이다. 투자를 모델링해 주는 클래스 라이브러리를 가지고 작업을 한다고 가정하자. 이 라이브러리는 Investm..
hvv_an
'effective C++' 태그의 글 목록 (4 Page)