Effective C++ 17

[Effective C++] 항목 17: new로 생성한 객체를 스마트 포인터에 저장하는 코드는 별도의 한 문장으로 만들자

1. 개요이번 장의 내용은 컴파일러마다 실행순서가 달라져서 발생하는 문제를 막는 방법에 대해 소개하는 항목입니다. 예를 들어보도록 하겠습니다.int Priority();void ProcessWidget(std::tr1::shared_ptr pw, int priority);이러한 함수 2개가 있다고 가정해 보겠습니다. 그리고 이렇게 만들어진 함수를 호출합니다. processWidget(new Widget, priority());일단 이것은 틀렸습니다. 왜냐하면 shared_ptr로 받고있기 때문에 자동으로 형변환이 안됩니다.(shared_ptr의 생성자는 explicit으로 선언되어 있습니다.) 그래서 아래와 같이 써야합니다.processWidget(std::tr1::shared_ptr(new Widge..

[Effective C++] 항목 16: new 및 delete를 사용할 때는 형태를 반드시 맞추자

1. 개요std::string *stringArray = new std::string[100];delete stringArray; 이 코드는 얼핏 보기에는 아무런 문제가 없어 보입니다. 메모리를 동적할당받고 잘 삭제해주고 있으니까요 그러나 이것은 완벽히 잘못된 코드입니다. 왜냐하면 동적할당으로 string객체를 100개만큼 생성해주고 있기 때문입니다. 위의 코드 처럼 한다면 1개의 객체 받게 해제시키지 못합니다. 2. new, delete의 동작방식new를 실행하면 2가지 동작이 수행됩니다. 1. 메모리가 할당됩니다.2. 할당된 메모리에 대해 한 개 이상의 생성자가 호출됩니다. ([]도 포함해서 그렇습니다.) delete도 비슷합니다. 1. 할당된 메모리에 대해 한개 이상의 소멸자가 호출됩니다.2. 메모..

[Effective C++] 항목 15: 자원 관리 클래스에서 관리되는 자원은 외부에서 접근할 수 있도록 하자

1. 개요자원 관리 클래스는 메모리 누수를 막는 아주 훌륭한 방법입니다. 그러나 우리는 종종 자원 관리 객체의 보호벽을 넘어서 실제 자원을 직접 만져야 할 일이 종종 생길것입니다. std::tr1::shared_ptr yptr(createYeoul());int YeoulAge(const Yeoul* ptr);int age = YeoulAge(yptr); // 에러 만약 코드를 이렇게 작성한다면 오류가 발생합니다. 왜냐하면 shared_ptr로 선언된 yptr과 Yeoul의 포인터는 형식이 맞지 않기 때문이지요 우리는 shared_ptr을 Yeoul*로 변환활 방법을 필요로 합니다. 2. 1번의 문제 해결법2-1. 명시적 형변환(explicit conversion)tr1::shared_ptr은 명시적으로 ..

[Effective C++] 항목 14: 자원 관리 클래스의 복사 동작에 대해 진지하게 고찰하자

1. 개요이번 항목은 자원관리 클래스를 만들때 복사 동작에 대해서 어떻게 처리해야할지를 다루는 문서입니다. 예를 하나 들어보도록 하겠습니다.void lock(Mutex* pm); // pm이 가리키는 mutex에 잠금을 겁니다.void unlock(Mutex* pm); //pm이 가리키는 mutex에 잠금을 풉니다. 이러한 mutex잠금을 관리하는 클래스를 만들려고 합니다. 이러한 클래스 역시 RAII 법칙을 따르게 됩니다. 생성시 자원을 획득하고 소멸 시에 자원을 해제하는 개념입니다. 아래는 이러한 클래스의 예제입니다.class Lock {public: explicit Lock(Mutex *pm) : mutexPtr(pm) { lock(mutexPtr); /..

[Effective C++] 항목 13: 자원 관리에는 객체가 그만!

1. 개요이번 장은 자원을 관리하는 법에 대해서 기술하는 챕터이다. 특히 항목 13은 포인터를 사용한뒤 해제하는 방법들을 소개한다. void f(){ Investment *pInv = createInvestment(); // 무언가 동작 delete pInv;} 이러한 코드를 보면 정상적으로 작동한다고 보일 수 있다. 그러나 중간에 있는 주석부분에 다양한 로직이 들어갈 수 있고 그때에 문제가 발생할 가능성이 있다. 2. 문제점과 auto_ptr저 코드가 정상적으로 delete까지 도달하면 아무런 문제가 없겠지만 그럴것이라는 보장이 절대 없다. 만약 중간에 다른 로직이 추가되어서 delete에 도달하기 전 return을 한다면 memory leak이 발생하게 된다. 혹은 예외등이 발생해..

[Effective C++] 항목 12: 객체의 모든 부분을 빠짐없이 복사하자

1. 개요설계가 잘 된 클래스를 보면 객체를 복사하는 함수는 딱 2개 있습니다. 1. 복사 생성자2. 복사 대입 연산자 우리는 이 둘을 통틀어서 복사 함수라고 부릅니다. 우리는 컴파일러가 자동으로 생성해주는 복사 함수 이외에 개발자가 따로 선언하는 것도 가능합니다. 그러나 컴파일러는 우리가 복사 함수를 잘못 선언해도 알려주지 않습니다. 그 예시를 한번 보도록 하죠 2. 복사 함수에 문제가 생길 때void logCall(const std::string& funcName); // make a log entryclass Customer {public: Customer(const Customer& rhs); Customer& operator=(const Customer& rhs);private: ..

[Effective C++] 항목 11: operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자

1. 개요자기대입은 어떤 객체가 자기 자신에게 대입 연산을 실행하는 것을 말합니다. 예를 들면 이런것이죠 Yeoul y; y=y; 문법상 아무 문제가 없는 코드입니다. a[i] = a[j]; i와j가 같아지면 자기대입이 됩니다. *pa = *pb; 둘이 가리키는 대상이 같으면 자기대입이 됩니다. 이러한 경우가 생기는 이유는 하나의 객체를 여러곳에서 참조하는 중복참조 상태 때문입니다. 이럴땐 같은 객체가 사용될 가능성을 고려하는것이 바람직한 자세입니다. void DoSomething(const Base& rb, Derived* pd);// 사실 rb와 *pb는 같은 객체였을지도 모릅니다. 결론은 자기 대입 연산은 생각보다 빈번하게 발생할 수 있다는 것이지요 2. 발생하는 문제들/해결법 2-1. 문제1자기대..

[Effective C++] 항목 10: 대입 연산자는 *this의 참조자를 반환하게 하자

1. 개요C++의 대입 연산자는 여러개가 동시에 엮일 수 있습니다. 무슨말인가 하면 우리는 일반적으로 대입연산자를 a=3; 이런식으로 씁니다. 그러나 a=b=c=3; 이런식으로 쓰는것도 가능하다는 말입니다. 우측 부터 연산이 진행되는 연산자입니다. 위의 연산을 괄호를 쳐서 본다면 이렇게 됩니다. a=(b=(c=3)); c에 3대입되고 그 결과가 b에 대입되고 다시 그 결과가 a에 대입됩니다. 이러한 연산이 가능하려면 연산자가 좌변에 대한 참조자를 반환하도록 구현되어 있어야 합니다.2. 다른것들은?좌변 객체의 참조자를 반환하자는 규칙은 = 뿐만 아니라 다른 경우에도 지켜져야 합니다. 예를들면 += -= *= 등과같은 연산고 *this를 반환하게 하는것이 관례입니다. 이 관례는 모든 기본제공 타입들이 따..

[Effective C++] 항목 9: 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자

1. 개요저자는 객체 생성, 소멸중에 가상 함수를 절대 호출하지 말라고 합니다. 그 이유는 무엇일까? 지금부터 알아 보도록 합시다. 2. 문제아래 코드를 보면 Transaction이 가장 최상위 클래스이고 하위로 BuyTransaction, SellTransaction이 각각 Transaction을 상속받고 있는 상태입니다. main에서는 BuyTransaciton의 객체를 만들어 주고 있습니다. BuyTransaction객체를 만들기 위해서는 부모 클래스의 생성자가 먼저 호출되야 합니다. 그렇게 부모클래스인 Transaction의 생성자가 호출되는데 여기서 문제가 발생합니다. 왜냐하면 Transaction의 생성자에서는 logTransaction이라는 가상함수를 호출중이기 때문입니다. 여기서 호출되는..

[Effective C++] 항목 8: 예외가 소멸자를 떠나지 못하도록 붙들어 놓자

1. 여러개의 예외는 처리가 곤란하다.만약 이러한 클래스가 있고 벡터에서 10개의 Yeoul객체를 가지고 있다고 생각해보자.class Yeoul{public: ~Yeoul(){}};void DoSomething(){ std::vectorv;} 벡터 v가 소멸될때에 벡터는 자신이 들고있던 Yeoul객체들을 소멸시켜야 할 책임을 가지고 있습니다. 만약에 첫번째 Yeoul객체를 소멸하던중 예외가 생겼다고 생각해봅시다. 나머지 9개의 객체도 소멸되어야함으로 벡터는 여전히 나머지 객체에 대해서 소멸자를 호출하게 됩니다. 그런데 두번째 Yeoul객체 역시 예외를 발생시키면 어떻게 될까요? 이렇게 예외가 2개이상이 되어버리면 C++에서 감당하기 어렵습니다. 이러한 동작의 원인은 바로 예외가 터지는 것을 내..