전체 글
[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에 오면서 두 가지의 특수 멤버 함수가 추가되었는데, 이 특수 멤버 함수의 조건을 알아보자. 이동 생성자와 이동 배정 연산자 ..
[Effective Modern C++] 16. const멤버 함수를 스레드에 안전하게 작성하자
[Effective Modern C++] 16. const멤버 함수를 스레드에 안전하게 작성하자
2022.09.11멤버 함수가 멤버 변수들을 수정하지 않는다면 const로 선언하는 것이 자연스럽다. 그런데 스레드를 사용하면 문제가 생길 수 있다. 아래의 예시를 봐보자. mutex 사용 다음은 다항식의 근을 구하는 함수 roots를 구현하는 내용이다. 성능 향상을 위해 캐싱을 이용하였으며, rootsAreValid의 값을 이용하여 캐싱 여부를 판별한다. class Polynomial { public: using RootsType = std::vector; RootsType roots() const { if(!rootsAreValid) { ... rootsAreValid = true; } } private: mutable bool rootsAreValid { false }; mutable RootsType rootVal..
[Effective Modern C++] 15. 가능하면 항상 constexpr을 사용하자
[Effective Modern C++] 15. 가능하면 항상 constexpr을 사용하자
2022.09.11constexpr은 객체에 사용하면 const의 향상된 버전이지만 함수에 사용하면 다른 의미를 가진다. 그러나 일반적으로 constexpr이 나타내는 의미는 컴파일 시간에 알려진 값을 나타낸다. constexpr 객체 constexpr은 객체를 const로 만들고, LiteralType(값이 알려져 있고 컴파일 시점에 사용할 수 있는 객체)으로 만든다. 이렇게 컴파일 중에 알려진 값은 여러 권한이 있는데 예를 들어 읽기 전용 메모리에 저장될 수 있으며 불변의 상수 표현식이 필요할 때 대신 사용할 수도 있다.(배열 크기나 템플릿 매개변수(std::array 객체 길이 포함), 열거형 멤버의 값, 정렬 지정자 등이 포함된다.) 이러한 작업을 수행하기 위해 변수를 사용하려는 경우 컴파일러에서 컴파일 시점의 값..
[Effective Modern C++] 14. 예외를 방출하지 않을 함수는 noexcept로 선언하자
[Effective Modern C++] 14. 예외를 방출하지 않을 함수는 noexcept로 선언하자
2022.09.04noexcept는 함수 인터페이스의 일부이다. 이는 호출자가 noexcept 여부에 의존할 수 있음을 뜻한다. C++98에서 예외 지정은 함수 구현이 바뀌면 예외 지정도 바뀔 가능성이 있었고, 기존의 예외 지정에 의존하던 클라이언트 코드는 깨질 수도 있어 예외 지정이 득보다 실이 크다고 판단되었다. 하지만 C++11에서, 함수의 예외 방출 행동에 관해 정말로 의미 있는 정보는 함수가 예외를 하나라도 던질 수 있는지 아니면 절대로 던지지 않는지의 여부라는 점이라는 의견이 중요해졌는데, 본질적으로 C++98의 것을 대체하는 C++11의 예외 지정에는 바로 그러한 흑백논리가 깔려 있다.(C++98 스타일의 예외 지정도 여전히 유효하나, 비권장(deprecate) 기능으로 분류되었다) 결과적으로, C++11에서..
[Effective Modern C++] 13. iterator보다 const_iterator를 선호하자
[Effective Modern C++] 13. iterator보다 const_iterator를 선호하자
2022.09.04C++98에서 const_iterator는 그리 실용적이지 않았다. 왜냐면, 비상수 컨테이너로부터 const_iterator를 얻는 방법도 까다로웠고, 삽입과 삭제 위치를 iterator로만 지정할 수 있었기 때문이다. 하지만 C++11에서는 C++98에서의 문제점들이 개선되었다. 따라서 C++11이상의 환경에서 프로그래밍을 한다면 가능한 iterator보다 const_itertaor를 사용하는 것이 좋다. // 예를 들어 std::vector에서 1983이라는 값이 처음 나오는 지점을 찾고 그 곳에 1998이라는 값을 삽입한다고 하자. // 벡터에 1983이 하나도 없으면 1998을 벡터의 끝에 삽입해야 한다. // C++98에서는 iterator를 사용하여 간단하게 구현할 수 있다. std::vect..
[Effective Modern C++] 12. 재정의 함수들은 override로 선언하자
[Effective Modern C++] 12. 재정의 함수들은 override로 선언하자
2022.09.04재정의가 일어나려면 다음과 같은 여러 필수조건을 만족해야 한다. 기반 클래스 함수가 반드시 가상 함수이어야 한다. 기반 함수와 파생 함수의 이름이 반드시 동일해야 한다(단, 소멸자는 제외). 기반 함수와 파생 함수의 매개변수 타입들이 반드시 동일해야 한다. 기반 함수와 파생 함수의 const성이 반드시 동일해야 한다. 기반 함수와 파생 함수의 반환 타입과 예외 지정(exception specification)이 반드시 호환되어야 한다. [C++11에서 추가된 조건] 멤버 함수들의 참조 한정사(reference qualifier)들이 반드시 동일해야 한다. 이러한 모든 재정의 요구조건들이 뜻하는 것은, 작은 실수가 큰 차이를 빚을 수 있다는 것이다. 재정의 실수가 포함된 코드는 프로그래머가 의도한 것과는 ..
[Effective Modern C++] 11. 정의되지 않은 비공개 함수보다 삭제된 함수를 선호하자
[Effective Modern C++] 11. 정의되지 않은 비공개 함수보다 삭제된 함수를 선호하자
2022.09.03C++98에서는 어떤 함수의 사용을 막고 싶으면, private으로 선언하고 함수의 정의를 만들지 않는 방법을 사용하였다. // C++98 template class basic_ios : public ios_base { public: ... private: basic_ios(const basic_ios& ); // 정의 안함 basic_ios& operator=(const basic_ios&); // 정의 안함 }; 이렇게 private로 선언하면 클라이언트가 함수를 호출할 수 없다. 즉, 멤버 함수나 friend클래스가 정의하지 않은 함수를 사용하면 링크는 함수 정의 누락으로 인해 실패한다. C++11에서는 더 좋은 방법이 있는데, 사용을 막을 함수 선언의 끝에 "=delete"를 붙이면 된다. 이러한..
[Effective Modern C++] 10. 범위 없는 enum보다 범위 있는 enum을 선호하자
[Effective Modern C++] 10. 범위 없는 enum보다 범위 있는 enum을 선호하자
2022.08.28C++98 스타일의 enum을 이제는 범위 없는 enum이라고 부른다. C++98 스타일의 enum으로 선언된 열거자들에 대해서는 "한 중괄호 쌍 안에서 어떤 이름을 선언하면 그 이름의 가시성은 해당 중괄호 쌍이 정의하는 범위로 한정된다"는 일반적인 규칙이 적용되지 않는다. 그런 열거자 이름들은 enum을 포함하는 범위에 속하며, 따라서 그 범위에 같은 이름이 있으면 안된다. 이러한 이유로 C++98스타일인 enum은 범위 없는 enum이라고 부른다. enum Color { black, white, red }; // black, white, red는 Color가 속한 범위에 속함 auto white = false; // 오류! 이 범위에 이미 white가 선언되어 있음 범위 있은 enum의 열거자들은 그..