Books/Effective Modern C++
[Effective Modern C++] 42. 삽입 대신 생성 삽입을 고려하라
[Effective Modern C++] 42. 삽입 대신 생성 삽입을 고려하라
2022.10.23생성 삽입 함수들은 삽입 함수들이 하는 모든 일을 할 수 있다. 게다가 좀 더 효율적으로 수행될 수 있는 경우가 있으며, 적어도 이론적으로는, 덜 효율적으로 수행되는 경우는 결코 없다. std::vector vs; vs.push_back("xyzzy"); // 해석 ver vs.push_back(std::string("xyzzy")); // emplace_back은 완벽 전달을 이용하므로 문자열 리터럴이 vector안의 string생성자에게 그대로 전달된다. // 임시 객체의 생성과 소멸 비용이 들지않음 vs.emplace_back("xyzzy"); 생성삽입이 삽입의 성능을 능가하는 경우 1. 추가할 값이 컨테이너에 대입되는 것이 아니라 컨테이너 안에서 생성된다. 위의 예시에서 첫 코드 블럭의 "xyzz..
[Effective Modern C++] 41. 이동이 저렴하고 항상 복사되는 복사 가능 매개변수에 대해서는 값 전달을 고려하라
[Effective Modern C++] 41. 이동이 저렴하고 항상 복사되는 복사 가능 매개변수에 대해서는 값 전달을 고려하라
2022.10.23매개변수 전달 방법을 3가지로 나누어 성능을 비교해보자. // 오버로딩 class Widget { public: void addName(const std::string& newName) { names.push_back(newName); } void addName(std::string&& newName) { names.push_back(std::move(newName); } ... private: std::vector names; }; // 보편 참조 class Widget { public: template void addName(T&& newName) { names.push_back(std::forward(newName)); } ... }; // 값 전달 class Widget { public: void..
[Effective Modern C++] 40. 동시성에는 std::atomic을 사용하고, volatile은 특별한 메모리에 사용하라
[Effective Modern C++] 40. 동시성에는 std::atomic을 사용하고, volatile은 특별한 메모리에 사용하라
2022.10.23atomic과 volatile의 차이점에 대해 알아보자. atomic vs volatile std::atomic은 뮤텍스 보호 없이 여러 스레드가 접근하는 데이터를 위한 것으로, 동시적 소프트웨어의 작성을 위한 도구이다. 보통 뮤텍스보다 더 효율적인 기계어 명령들로 구현된다. 아래의 예시를 보자. std::atomic ai(0); // ai를 0으로 초기화 ai = 10; // 원자적으로 ai를 10으로 설정 std::cout
[Effective Modern C++] 39. 일회성(one-shot) 사건 통신에는 void future 객체를 고려하라
[Effective Modern C++] 39. 일회성(one-shot) 사건 통신에는 void future 객체를 고려하라
2022.10.23조건변수 기반 설계 스레드 간 통신을 수행할 때, 조건 변수(condition variable, 줄여서 condvar)를 사용하는 경우가 많다. 조건을 검출하는 과제를 검출 과제(detecting task)라고 부르고, 그 조건에 반응하는 과제를 반응 과제(reacting task)라고 부르도록 한다. 예시를 보자. std::condition_variable cv; // 조건변수 std::mutex m; // cv와 함께 사용할 뮤텍스 // 사건 검출 ... // 반응해야 하는 과제에게 알림. 반응 task가 여러개라면 notify_all을 사용하면 됨 cv.notify_one(); // 반응 task 개념 접근방식 ... { // 임계 영역을 열고, 뮤텍스를 잠근다 std::unique_lock lk(..
[Effective Modern C++] 38. 스레드 핸들 소멸자들의 다양한 행동 방식을 주의하라
[Effective Modern C++] 38. 스레드 핸들 소멸자들의 다양한 행동 방식을 주의하라
2022.10.23합류 가능한 스레드는 바탕 시스템의 실행 스레드에 대응된다. 스레드와 비슷한 async의 future 객체도 시스템 스레드에 대응된다. 따라서 std::thread 객체와 future객체 모두 시스템 스레드에 대한 핸들이라고 할 수 있다. 그리고 join가능 스레드를 파괴하면 프로그램이 종료된다.(항목 37 참고) 하지만 future객체의 소멸자는 어떨 때는 암묵적으로 join을 수행한 것과 같은 결과를 내고 어떨 때에는 암묵적으로 detach를 수행한 것과 같은 결과를 내지만, 프로그램이 종료되는 일은 없다. 우선, future객체는 피호출자가 결과를 호출자에게 전송하는 통신 채널의 한쪽 끝이라는 점을 주목해야 한다. 피호출자는 보통은 std::promise객체를 통해서 자신의 계산 결과를 그 통신 채..
[Effective Modern C++] 23. std::move와 std::forward를 숙지하라
[Effective Modern C++] 23. std::move와 std::forward를 숙지하라
2022.09.24std::move와 std::forward는 그냥 캐스팅을 수행하는 함수 템플릿이다. std::move 함수의 이름만 보면 std::move가 이동을 수행해 줄 것처럼 보이지만 위에서 말했던 것처럼 사실 타입 캐스팅이 전부이다. 그런데도 이 함수의 이름이 move인 이유는 rvalue로 캐스팅을 하기 때문이고, 아래의 코드는 std::move를 구현한 코드이다. // C++11 template typename remove_reference::type&& move(T&& param) { using ReturnType = typename remove_reference::type&&; return static_cast(param); } // C++14 더 단순해진 move구현 template decltype(..
[Effective Modern C++] 22. Pimpl 관용구를 사용할 때에는 특수 멤버 함수들을 구현 파일에서 정의하라
[Effective Modern C++] 22. Pimpl 관용구를 사용할 때에는 특수 멤버 함수들을 구현 파일에서 정의하라
2022.09.18Pimpl이란 pointer to implementation을 의미한다. // 기존 class Widget { public: Widget(); ... private: std::string name; std:;vector data; Gadget g1, g2, g3; }; // Pimpl class Widget { public: Widget(); ~Widget(); ... private: struct Impl; Impl *pImpl; }; Pimpl 적용 코드를 보면 string, vector, Gadget을 선언하고 있지 않아 #include 할 필요가 없어져 컴파일 속도가 빨라지게 되었다. 이렇게 선언만 하고 정의를 하지 않는 형식을 불완전한 형식이라고 하며, 구현부에 작성하면 된다. #include "..
[Effective Modern C++] 21. new를 직접 사용하는 것보다 std::make_unique와 std::make_shared를 선호하라
[Effective Modern C++] 21. new를 직접 사용하는 것보다 std::make_unique와 std::make_shared를 선호하라
2022.09.18std::make_shared는 C++11의 일부이지만, std::make_unique는 C++14에서 표준 라이브러리에 포함되었다. 하지만 C++11에서 make_unique와 같은 함수 템플릿을 만드는 것은 어렵지 않다. template std::unique_ptr make_unique(Ts&&... params) { return std::unique_ptr(new T(std::forward(params)...)); } make함수는 임의의 개수와 타입 인수들을 받아서 생성자로 전달한 후 객체를 동적으로 생성하고 그 객체를 가리키는 스마트 포인터를 돌려주는 함수이고 총 세 가지가 존재한다.(make_unique, make_shared, allocate_shared) 선호하는 이유 간결한 코드 auto..
[Effective Modern C++] 20. std::shared_ptr처럼 작동하되 대상을 잃을 수도 있는 포인터가 필요하면 std::weak_ptr를 사용하라
[Effective Modern C++] 20. std::shared_ptr처럼 작동하되 대상을 잃을 수도 있는 포인터가 필요하면 std::weak_ptr를 사용하라
2022.09.18weak_ptr weak_ptr는 언제든 소멸될 수 있는 객체를 사용할 때 쓰는 포인터이다. 대체로 weak_ptr은 shared_ptr을 이용해서 생성하지만 reference count를 증가시키지 않는다. 즉, 객체의 소멸에 관여하지 않는다. 대상을 잃은 weak_ptr은 만료되며 만료 여부는 멤버 함수 expired가 돌려주는 값으로 판단할 수 있다. 만료되지 않은 weak_ptr이라고 해도 역참조 연산이 없기 때문에 피지칭 객체에 직접 접근하는 것은 불가능하다. 만일 역참조 연산이 가능하게 하도록 한다고 하면, 사용하려고 하는 순간 이미 객체가 소멸되어 미정의 행동이 나올 수도 있다. // std::weak_ptr가 가리키는 피지칭 객체를 역참조 하려면 std::weak_ptr로부터 std::sh..
[Effective Modern C++] 19. 소유권 공유 자원의 관리에는 std::shared_ptr를 사용하라
[Effective Modern C++] 19. 소유권 공유 자원의 관리에는 std::shared_ptr를 사용하라
2022.09.18shared_ptr의 특징 shared_ptr은 unique_ptr과 다르게 여러 객체에서 한 객체를 소유할 수 있다. shared_ptr은 객체의 소멸 시점을 관리하기 위하여 reference count를 사용하는데, 이 count가 0이 되면 메모리를 해제한다. shared_ptr의 내부 구현은 다음과 같다. shared_ptr의 크기는 raw포인터의 두배이다. raw포인터의 크기와 reference count를 가리키는 포인터를 가지고 있어야 하기 때문이다. 참조 횟수를 담을 메모리를 반드시 동적으로 할당해야 한다. reference count를 가리키는 객체는 공유되는 자원이기 때문에 동적으로 할당해야 한다. 참조 횟수의 증가와 감소가 반드시 원자적(atomic) 연산이어야 한다. 멀티스레드 프로..
[Effective Modern C++] 18. 소유권 독점 자원의 관리에는 std::unique_ptr를 사용하라
[Effective Modern C++] 18. 소유권 독점 자원의 관리에는 std::unique_ptr를 사용하라
2022.09.18std::unique_ptr은 독점적 소유권 의미론을 재현하는 클래스이다. null이 아닌 std::unique_ptr은 항상 자신이 가리키는 개체를 소유한다. 그에 따라 복사를 허용하지 않으며, 오직 이동만 가능하다. 그리고 raw포인터와 거의 같은 크기를 가지기 때문에 메모리와 CPU 성능이 넉넉하지 않더라도 사용하기에 충분하다. std::unique_ptr객체는 자신이 파괴될 때, 가리키는 자원 또한 함께 파괴된다. std::unique_ptr객체의 파괴는 delete를 통해 일어나나 커스텀 삭제자를 사용해서 삭제할 수도 있는데 상태가 있는 삭제자나 함수포인터를 사용하면 std::unique_ptr의 크기가 커진다. auto delInvmt = [](Investment* pInvestment) { ..
[Effective Modern C++] 17. 특수 멤버 함수들의 자동 작성 조건을 숙지하자
[Effective Modern C++] 17. 특수 멤버 함수들의 자동 작성 조건을 숙지하자
2022.09.11특수 멤버 함수 컴파일러가 스스로 작성할 수 있는 멤버 함수들(기본 생성자, 소멸자, 복사 연산자들, 이동 연산자들)을 가리켜 특수 멤버 함수라고 부른다. 이 함수들은 클래스에 명시적으로 선언되어 있지는 않지만 이 함수들을 사용하는 클라이언트 코드가 존재할 때에만 생성된다. 작성된 특수 멤버 함수들은 암묵적으로 public이며 inline이다. 그리고 가상 소멸자가 있는 base class를 상속하는 파생 클래스의 소멸자를 제외하고는 non-virtual이다. 가상 소멸자가 있는 base class를 상속하는 경우 파생 클래스의 소멸자는 virtual로 선언된다. 그리고 C++11에 오면서 두 가지의 특수 멤버 함수가 추가되었는데, 이 특수 멤버 함수의 조건을 알아보자. 이동 생성자와 이동 배정 연산자 ..