Books/Effective C++
[Effective C++] 29. 예외 안전성이 확보되는 그날 위해 싸우고 또 싸우자!
[Effective C++] 29. 예외 안전성이 확보되는 그날 위해 싸우고 또 싸우자!
2022.06.19예외 안전성의 조건 소프트웨어 시스템은 예외에 안전하거나, 예외에 뚫려있거나 둘 중 하나이다. 아래의 예시를 보자. class PrettyMenu{ public: // 배경그림을 바꾸는 멤버함수 void changeBackground(std::istream& imgSrc); private: Mutex mutex; // 이 객체 하나를 위한 뮤텍스 Image *bgImage; // 현재의 배경그림 int imageChanges; // 배경그림이 바뀐 횟수 }; void PrettyMenu::changeBackground(std::istream& imgSrc) { lock(&mutex); // 뮤텍스 획득 delete bgImage; // 이전 배경그림 없앰 ++imageChanges; // 그림 변경횟수 ..
[Effective C++] 28. 내부에서 사용하는 객체에 대한 '핸들'을 반환하는 코드는 되도록 피하자
[Effective C++] 28. 내부에서 사용하는 객체에 대한 '핸들'을 반환하는 코드는 되도록 피하자
2022.06.18예시로 사각형 클래스로부터 시작해보자. 메모리 효율을 높이기 위해 꼭짓점(Point)을 별도의 구조체(RectData)에 넣어 사각형 클래스(Rectangle)가 이를 가리키도록 해보자. class Point { public: Point(int x, int y); void setX(int newVal); void setY(int newVal); }; struct RectData { Point ulhc; // 좌측상단 Point lrhc; // 우측하단 }; class Rectangle { private: std::tr1::shared_ptr pData; public: // 아래 두행은 전부 상수멤버함수임을 기억하자. Point & upperLeft() const { return pData->ulhc; }..
[Effective C++] 27. 캐스팅은 절약, 또 절약! 잊지 말자
[Effective C++] 27. 캐스팅은 절약, 또 절약! 잊지 말자
2022.06.18'어떤 일이 있어도 type error가 생기지 않도록 보장한다.'라는 것은 C++의 동작 규칙이다. 즉, 이론적으로는 컴파일만 잘 되면 그 이후엔 어떤 객체에서도 불완전한 연산이나 말도 안 되는 연산 등을 수행하지 않는다는 것이다. 하지만 C++에서는 cast 시스템 때문에 이런 보장이 깨질 수도 있다. 캐스트 방법 C 스타일의 캐스트 (구형 캐스트) (T) 표현식 T(표현식) : 함수 방식의 캐스트 C++ 스타일의 캐스팅 const_cast(표현식) 객체의 상수성을 없애는 용도, const -> non-const로 바꾸는 용도 dynamic_cast(표현식) 안전한 다운 캐스팅을 할 때 사용한다. 상속 상황에서 쓰이며, 주어진 객체가 어떤 클래스 상속 계통에 속한 특정 타입인지 아닌지를 결정하는 작업..
[Effective C++] 26. 변수 정의는 늦출 수 있는 데까지 늦추는 근성을 발휘하자
[Effective C++] 26. 변수 정의는 늦출 수 있는 데까지 늦추는 근성을 발휘하자
2022.06.18생성자 혹은 소멸자를 끌고 다니는 타입으로 변수를 정의하면 반드시 물게 되는 비용이 두 개 있다. 프로그램 제어 흐름이 변수의 정의에 닿을 때 생성자가 호출되는 비용, 변수가 유효 범위를 벗어날 때 소멸자가 호출되는 비용이다. 이러한 비용은 변수가 정의만 돼도 부과된다. 예제를 보자. std::string encryptPassword(const std::string& password) { using namespace std; string encrypted; if(password.length() < MinimumPasswordLength) { throw logic_error("Password is too short"); } return encrypted; } encrypted를 안 쓴다고 할 순 없지만, ..
[Effective C++] 25. 예외를 던지지 않는 swap에 대한 지원도 생각해 보자
[Effective C++] 25. 예외를 던지지 않는 swap에 대한 지원도 생각해 보자
2022.06.12std의 표준 swap namespace std { template void swap(T& a, T& b) { T temp(a); a = b; b = temp; } } 위에서 보는 것처럼 표준 swap은 복사 생성자와 복사 대입 연산자를 통해 이루어지는데, 복사만 지원하는 타입이라면 어떤 객체든 맞바꾸기 동작을 수행해주며 타입에 상관없이 1번 호출에 3번 복사가 일어난다. (a에서 temp, b에서 a, temp에서 b) 표준 swap의 단점 위에서 말한 것처럼 표준 swap은 1번 호출에 3번 복사가 이루어지기 때문에 다른 타입의 실제 데이터를 가리키는 포인터가 주성분 타입일 경우 복사 시 손해를 보게 된다. 이런 개념을 설계의 미학으로 끌어올려서 사용하는 기법이 pumpl 관용구이다. class Wi..
[Effective C++] 24. 타입 변환이 모든 매개변수에 대해 적용되어야 한다면 비멤버 함수를 선언하자
[Effective C++] 24. 타입 변환이 모든 매개변수에 대해 적용되어야 한다면 비멤버 함수를 선언하자
2022.06.12타입 변환 예시 이전 내용에서 암시적 타입 변환을 지원하는 것은 좋지 않다.라는 내용을 기억할 수 있다. 하지만 가장 흔한 예외 중 하나가 숫자 타입을 만들 때이다. 예를 들어 유리수를 나타내는 클래스를 만들고 있다면, 정수에서 유리수로의 암시적 변환은 허용하자고 판단하더라도 크게 이상하지 않다. 아래의 예시를 봐보면, class Rational { public: Rational(int numerator = 0, int denominator = 1); // 암시적 변환을 위해 explicit를 붙이지 않음. int numerator() const; int denominator() const; private: .... }; 위의 클래스는 int에서 Rational로의 암시적 변환을 허용한다. 이 클래스에서..
[Effective C++] 23. 멤버 함수보다는 비멤버 비프렌드 함수와 더 가까워지자
[Effective C++] 23. 멤버 함수보다는 비멤버 비프렌드 함수와 더 가까워지자
2022.06.12캡슐화의 의미 어떤 것을 캡슐화하면, 우선 외부에서 이것을 볼 수 없게 된다. 그리고 캡슐화한 것들이 늘어나면 그만큼 밖에서 볼 수 있는 것들이 줄어드므로 다른 것들을 바꿀 때 필요한 유연성이 커진다. 즉, 해당 객체의 데이터에 접근할 수 있는 함수가 많을수록 캡슐화 정도는 낮다. 멤버 함수 vs 비멤버, 비프랜드 함수 class WebBrowser { public: void clearCache(); void clearHistory(); void removeCokies(); }; // 멤버 함수 - 한번에 처리 class WebBrowser(){ public: void clearEveryThing(); }; // 비멤버 함수 void clearBrowser(WebBrowser& wb) { wb.clear..
[Effective C++] 22. 데이터 멤버가 선언될 곳은 private 영역임을 명심하자
[Effective C++] 22. 데이터 멤버가 선언될 곳은 private 영역임을 명심하자
2022.06.12데이터 멤버를 private로 선언해야 하는 이유 1. 문법적 일관성 공개 인터페이스가 전부 함수뿐이라면, 클래스의 멤버에 접근할 때 괄호를 쓸지 말지 고민할 필요가 없다. 2. 세밀한 접근 제어 변수가 public이라면 읽기 및 쓰기 모든 것을 할 수 있지만, private로 만들면 읽기 전용, 쓰기 전용, 읽기 및 쓰기 전용 형식으로 구현할 수 있다. 즉, 접근 권한을 세밀하게 컨트롤 가능하다. class AccessLevels { public: int getReadOnly() const { return readOnly; } void setReadWrite(int value) { readWrite = value; } int getReadWrite() const { return readWrite; } ..
[Effective C++] 21. 함수에서 객체를 반환해야 할 경우에 참조자를 반환하려고 들지 말자
[Effective C++] 21. 함수에서 객체를 반환해야 할 경우에 참조자를 반환하려고 들지 말자
2022.06.03함수 수준에서 새로운 객체를 만드는 방법은 스택에 만들거나(지역변수), 힙에 만드는 것(동적 할당) 두 가지뿐이다. 함수에서 참조자를 반환할 때의 문제점 1. 스택 const Rational& operator*(const Rational& lhs, const Rational& rhs) { Rational result(lhs.n * rhs.n, lhs.d * rhs.d); return result; } result는 다른 객체처럼 생성자를 통해 생성된다. 그리고 result는 지역 변수이므로 operator*가 끝난 시점에 소멸된다. 즉, result는 이미 소멸된 메모리를 가리킨다. 2. 힙 const Rational& operator*(const Rational& lhs, const Rational& ..
[Effective C++] 20. 값에 의한 전달보다는 상수객체 참조자에 의한 전달 방식을 택하는 편이 대개 낫다
[Effective C++] 20. 값에 의한 전달보다는 상수객체 참조자에 의한 전달 방식을 택하는 편이 대개 낫다
2022.05.29값에 의한 전달 방식 기본적으로 C++은 함수로부터 객체를 전달받거나 함수에 객체를 전달할 때 값에 의한 전달(pass-by-value) 방식을 사용한다. 특별히 다른 방식을 지정하지 않은 함수 매개변수는 실제 인자의 사본을 통해 초기화되며, 어떤 함수를 호출한 쪽은 그 함수가 반환한 값의 사본을 돌려받는다. 이들 사본을 만들어 내는 것은 복사 생성자인데, 이 점 때문에 값에 의한 전달이 고비용의 연산이 되기도 한다. class Person { public: Person(); virtual ~Person(); ... private: std::string name; std::string address; }; class Student: public Person { public: Student(); ~Stud..
[Effective C++] 19. 클래스 설계는 타입 설계와 똑같이 취급하자
[Effective C++] 19. 클래스 설계는 타입 설계와 똑같이 취급하자
2022.05.29새로운 클래스를 정의한다는 것은 새로운 타입을 하나 정의하는 것과 같다. 효과적인 클래스 설계를 위한 질문들 1. 새로 정의한 타입의 객체 생성 및 소멸은 어떻게 이루어져야 하는가? 이 부분에 따라 클래스의 생성자와 소멸자의 설계가 바뀐다. 메모리 할당 함수(operator new, operator new [], operator delete, operator delete [])를 직접 작성할 경우 설계에 영향을 미친다. 2. 객체 초기화는 객체 대입과 어떻게 달라야 하는가? 생성자와 대입 연산자의 동작 및 둘 사이의 차이점을 결정짓는다. 초기화와 대입은 각각에 해당되는 함수 호출이 다르므로 헷갈리지 않는 것이 가장 중요하다. 3. 새로운 타입으로 만든 객체가 값에 의해 전달되는 경우에 어떤 의미를 줄 것..
[Effective C++] 18. 인터페이스 설계는 제대로 쓰기엔 쉽게, 엉터리로 쓰기엔 어렵게 하자
[Effective C++] 18. 인터페이스 설계는 제대로 쓰기엔 쉽게, 엉터리로 쓰기엔 어렵게 하자
2022.05.29제대로 쓰기엔 쉽고 엉터리로 쓰기엔 어려운 인터페이스를 개발하려면 우선 사용자가 저지를 만한 실수의 종류를 머리에 넣어두고 있어야 한다. 사용자 타입 시스템을 활용한 인터페이스 아래 예는 날짜를 나타내는 어떤 클래스에 넣을 생성자를 설계하는 과정이다. class Date { public: Date(int month, int day, int year); ... }; 문제점 1. 매개변수의 전달 순서가 잘못될 여지가 있다. Date d(30, 3, 2022); // 일과 월을 바꿔서 입력함 2. 월과 일에 해당하는 숫자가 잘못된 숫자일 수 있다. Date d(3, 40, 2022); 3월 40일은 없다. 해결방안 1. 새로운 타입을 들여와 인터페이스를 강화하여 사용자의 실수를 막는다. struct Day {..