분류 전체보기

좋은 인터페이스는 제대로 쓰기엔 쉽고 틀리게 쓰기엔 어렵다. 인터페이스를 만들 때는 이 특성을 지닐 수 있도록 고민해야 한다. 사용자의 실수를 방지하는 방법으로는 새로운 타입 만들기, 타입에 대한 연산 제한, 객체의 값에 대해 제약 걸기, 자원 관리 작업을 사용자 책임으로 놓지 않기가 있다. 인터페이스의 올바른 사용을 이끄는 방법으로는 인터페이스 사이의 일관성 잡아주기, 기본 제공 타입과의 동작 호환성 유지하기가 있다. 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..
객체 복사 함수는 주어진 객체의 모든 데이터 멤버 및 모든 기본 클래스 부분을 빠뜨리지 말고 복사해야 한다. 클래스의 복사 함수 두 개를 구현할 때, 한쪽을 이용해서 다른 쪽을 구현하지 마라. 그 대신, 공통된 동작을 제3의 함수에다 분리해 놓고 양쪽에서 호출해라. 객체를 복사할 때 주의할 점 객체의 안쪽 부분을 캡슐화한 객체 지향 시스템 중 설계가 잘 된 것들을 보면, 복사 함수가 복사 생성자와 복사 대입 연산자 딱 둘만 있다. 이러한 복사 함수는 컴파일러가 필요에 따라 만들어내기도 한다. 컴파일러가 만든 복사함수는 기본적인 요구에 충실히 동작한다. 복사되는 객체가 갖고 있는 데이터를 빠짐없이 복사한다. 객체 복사 함수를 선언하는 것은 컴파일러가 생성하는 것보다 추가적인 동작을 하기 위해서일 것이다. ..
operator=을 구현할 때, 어떤 객체가 그 자신에 대입되는 경우를 제대로 처리해야 한다. 원본 객체와 복사대상 객체의 주소를 비교해도 되고, 문장의 순서를 적절히 조정할 수도 있으며 복사 후 맞바꾸어도 된다. 두 개 이상의 객체에 대해 동작하는 함수가 있다면, 이 함수에 넘겨지는 객체들이 같은 객체인 경우에 정확하게 동작하게 해라. operator= 자기대입 처리 자기대입이란, 어떤 객체가 자기 자신에 대해 대입 연산자를 적용하는 것을 말한다. class Widget { ... }; Widget w; ... w = w; // 자기대입 이 코드는 문제가 없는 적법한 코드이다. 다음과 같은 상황에 많이 발생한다. a[i] = a[j]; // i==j라면 자기대입 *px = *py; // 가리키는 대상이..
대입 연산자는 *this의 참조자를 반환하도록 만들어라 대입 연산자는 *this의 참조자를 반환 C++의 대입 연산은 여러 개가 사슬처럼 엮일 수 있다. int x, y, z; x = y = z = 15; // 사슬처럼 이어짐 대입 연산이 가진 특성 중 하나는 우측 연관 연산이라는 점이다. 즉, 위의 대입 연산 사슬은 다음과 같이 분석된다. x = (y = (z = 15)); 15가 z에 대입되고, 그 대입 연산의 결과가 y에 대입된 후, y에 대한 대입 연산의 결과가 x에 대입된다. 이렇게 대입 연산이 사슬처럼 엮이려면 대입 연산자가 좌변 인자에 대한 참조자를 반환하도록 구현되어 있을 것이다. 이런 구현은 관례인데, 클래스의 대입 연산자를 구현할 때는 이 점을 지켜주는 것이 좋다. class Widge..
소멸자에서는 예외가 빠져나가면 안 된다. 만약 소멸자 안에서 호출된 함수가 예외를 던질 가능성이 있다면, 어떤 예외이든지 소멸자에서 모두 처리해야 한다. 어떤 클래스의 연산이 진행되다가 던진 예외에 대해 사용자가 반응해야 할 필요가 있다면, 해당 연산을 제공하는 함수는 반드시 소멸자가 아니어야 한다. 예외가 소멸자를 떠나지 않게 하자 소멸자로부터 예외가 터져 나가는 경우를 C++ 언어에서 막는 것은 아니지만, 실제 상황을 보면 확실히 막아야 하긴 하다. class Widget { public: ... ~Widget () { ... } // 이 함수에서 예외가 발생한다고 가정 }; void doSomething() { std::vector v; ... } // v 자동 소멸 vector 타입의 객체 v는 ..
hvv_an
'분류 전체보기' 카테고리의 글 목록 (37 Page)