effective-cpp (44) 썸네일형 리스트형 항목 18: 인터페이스 설계는 제대로 쓰기엔 쉽게, 엉터리로 쓰기엔 어렵게 하자 인터페이스 = 함수, 클래스, 템플릿 ….. 이걸 설계 신조로 삼자. "제대로 쓰기엔 쉽고, 엉터리로 쓰기엔 어렵게" 이걸 설계 신조로 삼자. 예를 들어, 날짜를 나타내는 어떤 클래스에 넣을 생성자를 설계하고 있다고 가정하자. 별 문제 없어보인다. 근데 인터페이스 설계 => 개발자의 눈이 아니라 사용자의 눈으로 봐야됨. 사용자가 어떤 실수를 저지를까? 가령 매개변수의 전달 순서가 잘못될 여지가 있다. Date d(30,3,1995); int가 될 수 있기 때문에(항목 2 참조) 타입의 안정성이 떨어진다는 단점이 있다, 타입 안전성이 신경 쓰인다면, 유효한 Month의 집합을 미리 정의해 두어도 괜찮다. 왜 굳이 객체가 아닌 함수로 했나? 이렇게 하면 안되나?? 다른 번역 단위에서 쓰일 경우 문제가 될 여.. 항목 17: new로 생성한 객체를 스마트 포인터에 저장하는 코드는 별도의 한 문장으로 만들자. 처리 우선순위를 알려 주는 함수가 하나 있고, 동적으로 할당된 Widget 객체(자원)에 대해 어떤 우선순위에 따라 처리를 적용하는 함수가 하나 있다고 가정해보자. 자원 관리에는 자원 관리 객체를 사용하는 것이 좋다 => Widget 객체에 대해 스마트 포인터를 사용하도록 만들었다. 이렇게 만들어진 processWidget을 호출해보자. tr1::shared_ptr의 생성자는 explicit으로 되어있기 때문에, 암시적 형 변환이 일어나지 않는다. 그래서 명시적 변환을 해줘야 한다. 이제 컴파일은 잘 되지만, 이 문장은 자원을 흘릴 가능성이 있다.(아니 자원 관리 객체를 쓰는데도 왜 자원이 샘?) 일단 첫번째 인자는 크게 두 개로 나눠어져 있음 "new Widget" 표현식을 쓰는 부분 tr1::shar.. 항목 16: new 및 delete를 사용할 때는 형태를 반드시 맞추자. std::string *stringArray = new std::string[100]; … delete stringArray; 여기서 잘못된 점이 보이냐? => 이 프로그램은 무조건 미정의 동작을 한다. 왜 why => stringArray가 가르키는 100개의 string 객체 중에 99개는 제대로 소멸 과정을 거치지 못할 것이기 때문이다. 우리가 new 연산자를 사용해서 표현식을 꾸미게 되면(즉, new로 어떤 객체를 동적 할당하면), 두 가지의 내부 동작이 진행된다. 메모리가 할당된다. 할당된 메모리에 대해 한 개 이상의 생성자가 호출된다. delete 표현식을 쓸 경우에는 기존에 할당된 메모리에 대해 한 개 이상의 소멸자가 호출된다. 메모리가 해제된다.(이때 operator delete라는 이름의.. 항목 15: 자원 관리 클래스에서 관리하는 자원은 외부에서 접근할 수 있도록 하자 자원 관리 클래스 == good class 실수로 터질 수 있는 자원 누출은 튼튼히 막아 주는 보호벽 역할을 한다. 똑바로 설계된 시스템이면 당연히 자원 누출은 없어야 한다. 하지만 세상은 만만치 않다. 현장에서 열심히 쓰이고 있는 수많은 API들이 자원을 직접 참조하도록 되있어서, 자원 관리 객체를 넘어가서 직접 자원을 주물러야 하는 일이 있을 수 밖에 없다… 우리는 dayHeld를 다음과 같이 쓰고 싶은데 타입이 안맞음 타입을 맞추는 방법 : 명시적, 암시적 반환 명시적: RAII 클래스(shared_ptr)는 get() 이라는 함수를 제공함, 이 함수를 사용하면 각 타입으로 만든 스마트 포인터 객체에 들어 있는 실제 포인터(의 사본)을 얻을 수 있다. 암시적: 제대로된 스마트 포인터들은 다 역참조 .. 항목 13: 자원 관리에는 객체가 그만! 만약 투자를 모델링 해주는 클래스 라이브러리를 가지고 어떤 작업을 한다고 생각해보자. 이 라이브러리는 Investment라는 최상위 클래스가 있고. 이것을 기본으로 구체적인 형태의 투자 클래스가 파생되어 있다. 또 이 라이브러리는 Investment에서 파생된 클래스의 객체를 사용자가 얻어내는 용도로 팩토리 함수(항목 7 참조)만을 쓰도록 만들어져 있다고 해보자. 객체의 해제는 사용자가 직접 해야 한다고 가정하면, 저렇게 delete pInv;를 할 것인데 당연히 f가 실행되면 delete문이 실행될 것이라고 믿으면 안됨 처음엔 무조건 delete문으로 코드가 흐르게 잘 해놨어도, 유지보수를 하면서 멍청한 다른 프로그래머가 continue와 return을 남발한다던지 해서 이 흐름을 박살냈을 수도 있다... 항목 12: 객체의 모든 부분을 빠짐없이 복사하자 애초에 Customer의 기본생성자가 없으면 에러남 왜 why, 어찌됐든 PriorityCustomer의 복사 생성자 호출 전에 Customer의 생성자가 호출될 탠데, PriorityCustomer 복사 생성자의 맴버 초기화 리스트에 기본 클래스 생성자에 넘길 인자들도 명시되지 않음 => 기본 클래스의 기본 생성자를 호출해야 하는데, Custom 클래스에는 기본 생성자가 없다. => Error 그리고 기본 생성자를 만들어도 이 기본 생성자는 복사가 아닌, 기본적인 초기화를 해줄 것인데 의미가 있나(인자가 없으니 당연한 것임) 그래서 만들어주자 그냥 복사함수에서는 모든 부분을 복사하자!!!!!! 코드중복을 피하고 싶은 변태들은 복사 대입연산자에서 복사 생성자를 호출하는 그런 방법을 떠올리는데 말이 안된다.. 항목 11: operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자 자기대입 = self assignment a=a; a[i] = a[j]; // i==j일시 자기대입 *pa = *pb; // pa와 pb가 같은 객체를 가르킬 시 자기대입 void doSomething(const Base& pb, Derived* pd) // pb와 pd가 같은 객체를 가르킬 가능성이 있음 모두 다 자기 대입의 가능성이 있거나, 자기 대입임 중복참조(aliasing) == 여러곳에서 하나의 객체를 가르킴 => 자기대입 가능성 참고) 왜 this == &t 를 비교하냐? 만약 t = t; 이렇게 되있으면(Self Copy) 저게 없으면 t를 지워버리고 다시 할당받을것을 가르키는데 문제는 복사할 구조체도 같이 없어지기 때문에 복사가 안됨 >> Self Copy이면 바로 Return 해버림 A.. 항목 10: 대입 연자는 *this의 참조자를 반환하게 하자 수식 => 값이 된다. 대입 연산의 결과 : 해당 변수에 저장된 값 우리가 만드는 클래스에서 대입 연산자가 들어간다면 이 규칙을 무조건 지켜야됨 x = y = z = 15 이면 처음에 z = 15, 그 다음 y = z, 그 다음 x = y 이다. 그럼 y=z에서 z는 갱신된 z이다.(z=15를 완료한) => 대입 연산자가 좌변 인자에 대한 참조자를 반환하도록 구현 대입 연산자는 *this의 참조자를 반환하도록 만들자. 항목 9: 객체 생성 및 소멸 과정 중에는 절대로 가상함수를 호출하지 말자 주식 거래를 본떠 만든 클래스 계통 구조가 있다고 하자. 매도, 매수 등등이 있어야 할 것인데 이러한 거래를 모델링할 때 꼭 감사(audit)기능이 있어야 한다. 그래서 어떤 주식 거래 객체가 생성될 때 마다 감사 로그에 적 절한 거래 내역이 나오게 하고 싶다. 그래서 다음과 같이 코드를 짰음. BuyTransaction의 생성자가 호출될 탠데, 생성자의 호출 순서는 base 클래스의 생성자가 먼저 호출되고 그 다음 derived 생성자가 호출되고 … 순서이다. base 클래스 생성자에 가상함수인 logTransaction을 호출하는 부분이 보이는데, 이때의 logTransaction은 BuyTransaction가 아니라 Transaction의 것이다. 기본 클래스의 생성자가 호출될 동안에는 가상 함수는.. 이전 1 2 3 4 5 다음