목록C++/Effective C++ (32)
방프리
항목 18 : 인터페이스 설계는 제대로 쓰기엔 쉽게, 엉터리로 쓰기엔 어렵게 하자 프로그래머도 사람입니다. 언제나 실수하기 마련이죠. 하지만 이 실수가 굉장히 큰 버그를 만들 수 도 있습니다. 예를 들어보겠습니다. class Date { public: Date(int month, int day, int year); }; Date d(30, 3, 1995); // 날짜와 월을 바꾸어서 초기화 만약 이렇게 잘못된 데이터가 들어갈 경우 그 결과는.... 여러분의 상상에 맡기겠습니다. 이러한 잘못된 코드를 최소한으로 줄여야 하기 때문에 맨 처음부터 인터페이스 설계를 탄탄하게 만들어야 합니다. struct Day { explicit Day (int d) : val(d) int val; }; struct Month..
항목 17 : new로 생성한 객체를 스마트 포인터에 저자하는 코드는 별도의 한 문장으로 만들자 하나의 예제 코드를 보고 시작하겠습니다. int priority(); void processWidget(std::tr1::shared_ptr pw, int priority); ... processWidget(new Widget, priority()); 자 간단한 코드입니다. 객체의 우선순위를 정하는 코드인데 priority() 함수를 통해 각 객체의 우선순위를 알아내고 processWidget을 통해 우선순위에 따라 처리를 하는 함수인 processWidget() 함수를 만들었습니다. 가만 보자 하니 processWidget() 함수에서 매개인자로 Widget 객체 포인터와 우선순위를 받는데 매개 인자를 동적..
항목 16: new 및 delete를 사용할 때는 형태를 반드시 맞추자 프로그래밍을 할 때 개발자는 한정적인 메모리 자원을 가지고 코딩을 하게 됩니다. 자원이 한정적이다 보니 메모리를 사용한다면? 다시 돌려놓기도 해야하죠. 이 과정을 C언어에서는 malloc, free로 이루어지고 C++에서는 new, delete를 통해 이루어집니다. 일방적으로 new만 계속 사용한다면? 진행은 되긴 됩니다만... 메모리 공간이 남아나질 않겠죠 ㅎㅎ 또 하나 반드시 알아야 할 것은 단일 객체의 생성일 때와 배열 객체의 생성일 때에 맞추어 new, delete도 다르게 해주어야 한다는 것입니다. 단일 객체일 때에는 delete, 배열 객체일 때에는 delete [] 를 꼭 사용해야 합니다. 만약 바꾸어서 하게 된다면 그에..
항목 15 : 자원 관리 클래스에서 관리되는 자원은 외부에서 접근할 수 있도록 하자. RAII방식으로 구현된 객체의 경우에 자원의 관리에 중점을 두었기 때문에 특성상 여러 군데에서 사용할 수 있어야 합니다. 또한 그 안에 있는 데이터도 접근이 가능해야 하죠. 그렇다 보니 RAII 클래스의 객체를 그 객체가 감싸고 있는 실제 자원으로 변환할 방법이 필요해집니다. 변환하는 방법은 보편적으로 명시적 변환과 암시적 변환 두 가지로 나뉘게 됩니다. 명시적 변환의 경우 get() 함수를 통해 자원의 상태를 반환하며, set() 함수를 통해 데이터의 조작을 이루어지게 합니다. 즉, 특정 함수를 통해 자원의 참조자를 얻고 얻은 데이터를 통해 조작이 이루어지게 하는 것이죠. 암시적 호출방식은 operator T()를 이..
항목 14 : 자원 관리 클래스의 복사 동작에 대해 진지하게 고찰하자 모든 자원은 힙에서 생성되지 않습니다. 그렇기 때문에 앞의 13항목에서 배운 auto_ptr 이나 tr::shared_ptr의 경우 사용할 수 없다는 큰 제약이 생겨버립니다. 그래서 RAII 객체가 복사가 되거나 관리를 할 때 어떤 조건을 충족해야하는지 알아보겠습니다. (1) 복사를 금지합니다. - RAII 객체가 복사되도록 놔두는 것은 말이 안됩니다. 특히 뮤텍스처럼 쓰레드의 동기화를 일으키는 객체의 경우 사본이 있는다는건 더더욱 말이 안됩니다. 이걸 방지하는 방법으로는 복사 함수를 private로 선언해 복사를 막는 방법이 있습니다. (2) 관리하고 있는 자원에 대해 참조 카운팅을 수행합니다. - 객체가 복사될 경우 사본을 생성하는..
항목 13 : 자원 관리에는 객체가 그만! C++에서는 메모리가 할당되면 당연히 메모리를 해제하는 코드도 넣어주어야 합니다. 기초 중에 기초죠. (여담으로 C#, Java의 경우엔 가비지 컬렉터가 있으니...) 이 항목에서 말하는 핵심 내용은 이 메모리의 할당 및 해제는 객체 자기 스스로가 해야한다는 것 입니다. 스택에 쌓인 메모리는 일정 범위(지역 함수 및 main 함수이겠죠)를 넘어가면 자동으로 해제가 됩니다. 만약 이 자원을 관리하는 주체가 객체 자기 스스로가 아닌 어떠한 함수가 해준다면 어떻게 될까요?? 정말 능력 있는 개발자가 관리한다면 빈틈없이 진행하겠죠 하지만 사람은 언제든지 실수하기 마련입니다. 자신이 메모리를 할당하고서도 해제하는 것을 잊어버릴 수도 있다는 것이죠. 또 다른 경우로는 예외..
항목 12 : 객체의 모든 부분을 빠짐없이 복사하자. 일반적으로 클래스를 생성하면 생성자, 소멸자, 복사 생성자, 복사 대입 연산자 총 4가지의 함수가 기본적으로 생성이 됩니다. 이 중 복사 생성자와 복사 대입 연산자는 합쳐서 복사 함수로 일컫는데 컴파일러가 기본적으로 생성해주지만 기본적으로 생성되는 복사 함수는 정말 문제가 많습니다. 왜일까요? 생성자와 소멸자는 아무 문제가 없는데 유독 복사 함수가 문제인 이유는 클래스는 컴파일러가 아닌 사용자가 제작했기 때문입니다. 그렇기 때문에 어떠한 행동이 맞는지 컴파일러 자신이 판단하기가 어려운 것이죠. 대충 복사 하라고 해서 복사를 했는데 그 과정에서 문제가 생겼다? 그 순간 총체적 난국이 생겨버립니다. 이쯤되면 "어떤 경우에 문제가 생기나요?" 라는 질문이 ..
항목 11 : operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자. 이 항목을 살펴보기 전 자기대입이라는 것이 대해 알아보겠습니다. 자기대입이란? 어떤 객체가 자기 자신에 대해 대입 연산자를 적용하는 것을 말합니다. int나 일반 데이터 타입의 경우 자체적으로 대입 행동의 정의가 되어 있지만 사용자 정의 class나 구조체의 경우 어떤 행동을 해야할 지 모르기 때문에 정의를 해주어야 합니다. 일반적으로 개발자들이 생각하는 대입 코드를 알아보겠습니다. Widget w, w1; w =w1; 보통의 데이터 타입일 경우 통할 수도 있겠지만 사용자 정의 클래스에서는 어림없는 문법이죠. 무슨 행동을 해야하는 지도 모르고 어떤 데이터를 대입해야할지 아무것도 모릅니다. (컴파일러는 천재가 아닙니다.) 다..