본문 바로가기

C++38

소유권 공유 자원의 관리에는 std::shared_ptr를 사용하라 공유 포인터 즉 std::shared_ptr를 통해서 접근되는 객체의 수명은 공유포인터가 shared ownership 의미론을 통해서 관리한다. 특정한 하나의 std::shared_ptr이 객체를 소유하는 것이 아니다. 그 대신 모든 std::shared_ptr는 객체가 더 이상 필요하지 않게 된 시점에서 객체가 파괴됨을 보장하려 한다. 객체가 가리키던 마지막 std::shared_ptr가 객체를 더 이상 가리키지 않게 되면 자신이 가리키는 객체를 파괴한다. 이를 위해 std::shared_ptr는 자원에 연관된 값을 참조 횟수로서 제어블록에 담아 관리한다. 보통 생성시 참조 횟수를 증가시키며, 소멸자는 감소시킨다. 복사 배정 연산자는 둘 모두 수행한다. 이동 생성의 경우 기존 std::shared_p.. 2020. 7. 13.
std::unique_ptr std::unique_ptr는 항상 자신이 가리키는 객체를 소유한다. std::unique_ptr를 이동시 소유권이 원본 포인터에서 대상 포인터로 옮겨진다. std::unique_ptr의 복사는 허용되지 않는다. 만약 복사가 가능하다면 두개의 std::unique_ptr가 같은 자원을 가리키며 두 포인터 모두 그 자원을 소유하고있다는 얘기가 된다. 그렇기 때문에 std::unique_ptr은 이동 전용(move-only) 형식이며 소멸시 자신이 가리키는 자원을 파괴한다. 기본적으로 파괴는 std::unique_ptr안에 있는 생포인터에 delete를 적용함으로써 수행된다. std::unique_ptr은 Hierachy 구조에서 객체를 생성하는 팩터리 함수의 반환형식으로 쓰기에 적합하다. hierachy .. 2020. 7. 12.
특수 멤버 함수들의 자동 작성 조건을 숙지하라 C++의 공식 어법에서는 특수 멤버 함수(special member function)이 있다. 세세한 조건들이 필요하긴 하지만 C++98에서는 기본 생성자, 소멸자, 복사 생성자, 복사 배정 연산자가 있다. 이 함수들은 꼭 필요한 경우에만, 즉 이 함수들이 클래스에 명시적으로 선언되어 있지는 않지만 이 함수들을 사용하는 클라이언트 코드가 존재할 때에만 작성된다. 기본 생성자는 클래스에 생성자가 하나도 선언되어 있지 않을 때 작성된다. 작성된 특수 멤버 함수들은 암묵적으로 public 이며 inline이며, 가상 소멸자가 있는 base class를 상속하는 파생 클래스의 소멸자를 제외하고는 nonvirtual이다. 가상 소멸자가 있는 base class를 상속하는 경우 파생 클래스의 소멸자는 virtual.. 2020. 7. 10.
const 멤버 함수를 스레드에 안전하게 작성하라 다항식의 근을 구하는 등 계산 비용이 큰 함수들은 꼭 필요할 때에만 계산하는 것이 바람직하다. 또한 중복해서 계산하는 것은 피해야 하며, 필요한 때만 근을 계산해서 캐시에 저장하고 그렇지 않을 때에는 캐시에 있는 값을 돌려주도록 함수를 구현하는 것이 좋다. class Polynomial{ public: using RootsType = std::vector; RootsType roots() const { if (!rootsAreValid){// 캐시가 유효하지 않으면 근들을 계산해서 rootVals에 저장 ... rootsAreValid = true; } return rootVals; } private: mutable bool rootsAreValid{ false }; mutable RootsType ro.. 2020. 7. 9.
가능하면 항상 constexpr을 사용하라 constexpr을 객체에 적용했을 때에는 const의 강화 버전처럼 작용하지만 함수에 적용했을때에는 다른 의미로 작용한다. constexpr은 단지 상수일 뿐만 아니라 컴파일 시점에서 알려진다는 점을 나타낸다. 컴파일 시점에서 알려지는 값들은 읽기 전용 메모리에 배치될 수 있다. 즉 상수이자 컴파일 시점에서 알려진 정수 값을 C++에서 정수 상수 표현식(const expression)이 요구되는 문맥에서 사용할 수 있다는 것이다. 배열 크기나 정수 템플릿 인수, 열거자 값 등이 그런 문맥에 해당한다. 그런 변수를 constexpr로 선언하면 컴파일러는 그것이 컴파일 시점 상수임을 보장해 주게 된다. int sz; constexpr auto arrSize1 = sz;// 오류! sz의 값을 컴파일 시점에.. 2020. 7. 8.
예외를 방출하지 않을 함수는 noexpect로 함수를 noexcept로 선언할 것인지 여부는 인터페이스 설계상 문제이다. 즉, 함수 호출자는 noexcept 여부에 의존할 수 있다는 것을 뜻한다. 예외를 방출하지 않음이 확실한 함수를 선언할 때 noexcept를 사용하지 않는 것은 인터페이스 명세가 허술함을 의미한다. 예외를 받게 되는 일이 없음을 약속하기 위한 표현법이다. int f(int x) throw();// C++98 버전 int f(int x) noexcept;// C++11 버전 실행 시점에서 예외가 f 바깥으로 나가게 되면 f의 에외 명세에 위반된다. C++98에서는 예외 명세가 위반되면 호출 스택이 f를 호출한 시점에 도달할 때까지 풀리며(unwind), 그 지점에서 몇 가지 동작이 취해진 후 종료된다(terminate). C++11.. 2020. 7. 7.