C++/Effective C++

25: 예외를 던지는 swap에 대한 지원도 생각해 보자 표준에서 기본적으로 제공하는 swap은 한 번 호출에 복사가 세 번 일어난다. namespace std { template void swap(T& a, T& b) { T temp(a); a = b; b = temp; } } 타입에 따라서는 이런 사본이 정말 필요 없는 경우도 있다. 복사하면 손해를 보는 타입들 중 으뜸을 꼽는다면 아마도 다른 타입의 실제 데이터를 가리키는 포인터가 주성분인 타입일 것이다. 그런 기법이 바로 'pimpl 관용구'이다. class WidgetImpl { private: int a, b, c; // 복사 비용이 높은 데이터들 vector v; }; class Widget { public: Widget(const Widge..
24: 타입 변환이 모든 매개변수에 대해 적용되어야 한다면 비멤버 함수를 선언하자 클래스에서 암시적 타입 변환을 지원하는 것은 일반적으로 못된 생각이다. 물론 이 규칙에도 예외가 있는데, 가장 흔한 예외 중 하나가 숫자 타입을 만들 때이다. 예를 들어 유리수를 나타내는 클래스를 만들고 있다면 정수에서 유리수로의 암시적 변환은 크게 어이없거나 하진 않을 것이다. class Rational { // 생성자에는 일부로 explicit를 붙이지 않았다. // int에서 Rational로의 암시적 변환을 허용하기 위해 Rational(int numerator = 0, int denointaor = 1); int numerator() const; int denominator() const; private: }; 유..
23: 멤버 함수보다는 비멤버 비프렌드 함수와 더 가까워지자 웹브라우저를 나타내는 클래스가 하나 있다고 가정하자. 캐시를 비우는 함수, 기록을 없애는 함수, 쿠키를 제거하는 함수 같은 이런 저런 함수를 통해 제공하는 기능이 많을 것이다. class WebBrowser { public: void clearCache(); void clearHistory(); void removeCookies(); }; 이 세 함수를 모아서 불러주는 함수도 준비해 둘 수 있을 것이다. class WebBrowser { public: void clearEverything(); // clearCache, clearHistory, removeCookies를 호출 }; 물론 이 기능은 비멤버 함수로 제공해도 된다. 객체의 멤버 함수..
22: 데이터 멤버가 선언될 곳은 private 영역임을 명심하자 우선 문법적 일관성이 첫 번째 이유이다. 데이터 멤버가 public이 아니라면, 사용자 족에서 어떤 객체를 접근할 유일한 수단은 멤버 함수이다. 사용자쪽에서는 접근하고 싶을 때 괄호를 붙여야 하는지 말아야 하는지를 고민할 필요가 없다. 데이터 멤버의 접근성에 대해 훨씬 정교한 제어를 할 수 있다. 데이터 멤버를 public으로 내놨다면 모두가 읽기 및 쓰기 접근권한을 갖게 되지만, 이 값을 읽고 쓰는 함수가 있다면 우리가 직접 권한을 구현할 수 있다. class AccessLevels { public: int getReadOnly() const { return readOnly; } int setReadWrite(int value) { re..
21: 함수에서 객체를 반환해야 할 경우에 참조자를 반환하려고 들지 말자 유리수를 나타내는 클래스가 하나 있다고 가정하자. 이 클래스에는 두 유리수를 곱하는 멤버 함수가 선언되어 있다. class Rational { public: Rational(int numerator = 0, int denominator = 1); private: int n, d; friend const Rational operator*(const Rational& lhs, const Rational& rhs); }; 이 클래스의 operator*는 곱셈 결과를 값으로 반환하고 있다. 여기서 Rational의 생성과 소멸에 들어가는 비용을 아끼기위해 참조자를 반환하도록 설계하면 어떤 문제가 발생할까? 참조자는 어떤 것에 대한 '또 다..
20: '값에 의한 전달'보다는 '상수객체 참조자에 의한 전달' 방식을 택하는 편이 대개 낫다 C++는 함수로부터 객체를 전달받거나 함수에 객체를 전달할 때 '값에 의한 전달(pass-by-value)' 방식을 사용한다. 특별히 다른 방식을 지정하지 않는 한, 함수 매개변수는 실제 인자의 '사본'을 통해 초기화된다. 이들 사본을 만들어내는 원천이 바로 복사 생성자인데, 이 점 때문에 '값에 의한 전달'은 고비용의 연산이 되기도 한다. class Person { public: Person(); virtual ~Person(); private: string name; string address; }; class Student : public Person { public: Student(); ~Student(..
19: 클래스 설계는 타입 설계와 똑같이 취급하자 새로운 클래스를 정의한다는 것은 새로운 타입을 하나 정의하는 것과 같다. 우리는 클래스 설계자로 그치지 않으며 타입 설계자라는 막강한 권위를 가지고 있다. 함수와 연산자를 오버로드하고, 메모리 할당 및 해제를 제어하며, 객체 초기화 및 종료처리를 정의하는 작업, 메모리 할당 및 해제를 제어하며, 객체 초기화 및 종료처리를 정의하는 작업, 이 모두가 우리의 손에 달려 있다. 좋은 타입은 일단 문법(syntax)가 자연스럽고, 의미구조(semantics)가 직관적이며, 효율적인 구현이 한 가지 이상 가능해야 한다. 효과적인 클래스를 설계할 때 고려사항은 무엇일까? 새로 정의한 타입의 객체 생성 및 소멸은 어떻게 이루어져야 하는가? 이 부분이 어떻게 되느냐에 ..
18: 인터페이스 설계는 제대로 쓰기엔 쉽게, 엉터리로 쓰기엔 어렵게 하자 인터페이스를 사용했을 때, 결과 코드가 사용자가 생각한 대로 동작하지 않는다면 그 코드는 컴파일되지 않아야 맞다. '제대로 쓰기에 쉽고 엉터리로 쓰기엔 어려운' 인터페이스를 개발하려면 우선 사용자가 저지를 만한 실수의 종류를 머리에 넣어두고 있어야 한다. 새로운 타입을 들여와 인터페이스를 강화하기 class Date { public: Data(int month, int day, int year); }; 위의 Data 클래스의 생성자는 사용자가 쉽게 저지를 수 있는 오류 구멍이 존재한다. 우선 매개변수의 전달 순서가 잘못될 여지가 열려 있다. Date d(3, 40, 1995); 월과 일에 해당하는 숫자가 어이없는 숫자일 수 있다...
17: new로 생성한 객체를 스마트 포인터에 저장하는 코드는 별도의 한 문장으로 만들자 처리 우선순위를 알려 주는 함수가 하나 있고, 동적으로 할당한 Widget 객체에 대해 어떤 우선순위에 따라 처리를 적용하는 함수가 하나 있다고 가정하자. int priority() void proceessWidget(str1::shared_ptr pw, int priority); 이렇게 만들어진 processWidget 함수를 호출하자. processWidget(new Widget, priority()); 위처럼 호출하면 컴파일이 안 된다. shared_ptr의 생성자는 explicit로 선언되어 있기 때문에, new Widget 으로 만들어진 포인터가 shared_ptr 타입의 객체로 바꾸는 암시적인 변환이 있을..
16: new 및 delete를 사용할 때는 형태를 반드시 갖추자 string* stringArray = new string[100]; ... delete stringArray 위의 코드는 문제가 없는 것 처럼 보이지만 큰 문제가 있다. stringArray가 가리키는 객체 중 99개는 정상적인 소멸 과정을 거치지 못할 가능성이 크다. 위의 코드는 소멸자가 99번 불릴 턱이 없기 때문이다. new 연산자의 내부 동작은 일단 메모리가 할당되고, 할당된 메모리에 대해 한 개 이상의 생성자가 호출된다. delete의 내부 동작은 우선 기존에 할당된 메모리에 대해 한 개 이상의 소멸자가 호출되고, 그 후체 그 메모리가 해제된다. 여기서 delete 연산자가 적용되는 객체는 대체 몇 개 일까? new로 힙에 만들..
KANTAM
'C++/Effective C++' 카테고리의 글 목록 (4 Page)