2022. 4. 21. 20:04ㆍProgramming/JAVA, C++, Go, Rust
- 목차
raw pointer의 단점
1) pointing 대상이 객체인지 array인지 알수 없음
2) 사용 후 소멸해야 하는지 아닌지 알 수 없음
3) delete를 사용해야 하는지 다른 방법을 사용해야 하는지 알 수 없음
4) delete 인지 delete[]인지 알 수 없음
5) 삭제를 한번만 했는지 보장하기 힘듦
6) dnagling pointer에 대해 처리 방법이 없음
C++11에는 4가지 type의 smart pointer
1) std::auto_ptr : C++98 용
auto_ptr은 move 시 null pointer로 set 됨
auto_ptr은 container에서 사용 불가
2) std::unique_ptr
unique_ptr은 move semantics 가능
3) std::shared_ptr
4) std::weak_ptr
unique_ptr
1) a small, move-only smart pointer
exclusive-ownership에 맞는 smart pointer임
copy가 불가능하며, move만 지원
2) custom deleter가 지정될 수 있음
custom deleter 지정 가능
auto delInvmt = [](Investment* pInvestment) // custom
{ // deleter
makeLogEntry(pInvestment); // (a lambda
delete pInvestment; // expression)
};
std::unique_ptr<Investment, decltype(delInvmt)> pInv(nullptr, delInvmt);
pInv.reset(new Investment);
3) unique_ptr을 shared_ptr로 전환 가능
std::shared_ptr<Investment> sp = makeInvestment(arguments);
shared_ptr에서는 unique_ptr을 얻을 수 없음
4) unique_ptr은 raw pointer에 성능과 차이가 거의 없음
shared_ptr
1) raw pointer의 2배 크기
referencing mechanism을 처리하기 때문
2) reference count 용 memory는 동작 할당됨
reference count는 객체에 associated
reference count는 동적으로 할당되는 control block에 저장됨
3) reference counting은 atomic 이어야 함
control block의 생성 시점
1) make_shared 시 생성
make_shared는 custom deleter 지정 불가능
아래의 방법으로만 지정 가능
std::shared_ptr<Widget> spw1(new Widget, loggingDel);
2) shared_ptr에 raw pointer 대입 시 생성
단, unique_ptr과 마찬가지로 raw pointer에서의 생성은 피해야함
3) unique_ptr 혹은 auto_ptr 로부터 shared_ptr이 생성될 때
이미 control block을 지닌 객체로부터 shared_ptr 생성시는 control block을 생성하지 않음
raw pointer를 지정해서 생성하는 것은 지양
아래와 같은 코딩이 가능하기 때문임
auto pw = new Widget;
std::shared_ptr<Widget> spw1(pw, loggingDel); // create a control block
std::shared_ptr<Widget> spw2(pw, loggingDel); // create a control block
-> 2회 제거 시도 됨
단점
1) array에 사용할 수 없음
shared_ptr<T[]> 가 없음
unique_ptr<T[]> 는 있으나 사용을 권장하지 않음 대신 array, list등 사용 권장
2) Control BLock의 cost
unique_ptr의 보통 2배 size를 heap에 할당
weak_ptr
사용 경우
1) 값을 임시로 caching 하던가 혹은 observing만 하는 경우
- 지니고 있다고 사용 시 unique_ptr로 전환 후 사용
2) circular referencing 발생 시
shared_ptr 끼리의 상호 참조는 서로 reference count를 지니기에 서로 소멸 시키지 않음 -> leak 야기
한쪽을 weak_ptr로 바꿔서 shared_ptr 소멸 시 weak_ptr은 dangle하게 만듦
(weak_ptr은 dangle 상태를 감지할 수 있음 - 자신의 dereference 상태를 감지)
감지 방법
auto spw = std::make_shared<Widget>();
std::weak_ptr<Widget> wpw(spw);
spw = nullptr;
if (wpw.expired()) <- expired
weak_ptr에서의 shared_ptr 생성 방법
std::shared_ptr<Widget> spw1 = wpw.lock();
단, shared_ptr 생성 시점에서 weak_ptr이 expired 면, shared_ptr도 null
make_unique와 make_shared 사용을 선호해야 함
make의 장점
1) 코드 중복 제거
std::shared_ptr<Widget> spw2(new Widget); <- 중복으로 Widget 지정
code size 줄임
2) 예외에 대한 안정성 확보
processWidget(std::shared_ptr<Widget>(new Widget), computePriority());
new Widget -> shared_ptr -> computePriority 실행
이 순서가 변경되면, (compiler에 따라)
new Widget -> computePriority -> shared_ptr
-> computePriority에서 exception 발생
아래와 같이 코딩하면 자원 누수 없음
processWidget(std::make_shared<Widget>(), computePriority());
3) control block 통합 객체
CBLK와 Widget 객체가 한번에 한 곳의 위치에 큰 덩어리로 메모리 할당 됨
크기 및 속도상에 장점이 있음
make의 단점
1) custom deleter 지정 불가
custom deleter를 지정하려면 shared_ptr/unique_ptr을 new로 생성해야 함
아래와 같이 코딩해야 함
std::shared_ptr<Widget> spw(new Widget, cusDel);
processWidget(spw, computePriority());
2) 초기화 리스트 미지원
auto upv = std::make_unique<std::vector<int>>(10, 20);
-> 값들이 모두 20인 열 개짜리 vector 한개를 생성
20, 20, 20, 20, 20, 20, 20, 20, 20, 20
의도한 것은 {10, 20}인데 다르게 생성됨
weak_ptr의 동작 차이
1) make로 생성한 경우
CBLK와 객체가 동일 관리되어 weak ptr의 reference가 0이 될때까지 객체도 해제 안 됨
2) new로 생성한 경우
CBLK와 객체가 별도 관리되어 weak ptr의 ref. cnt가 1이상이어도 shared_ptr의 ref cnt가 0이면 객체 소멸됨 (weak_ptr은 dangling 됨)
pimpl
pImpl은 생성/해제의 miss를 예방하기 위해 raw pointer보다는 smart pointer를 사용
pimpl의 장점은 haeder를 사용하는 client의 recompile 제거
pimpl 역시 move를 할 수 있으며, move를 하려면 아래의 move operation들을 선언해야 함
pimpl에 shared_ptr?
exclusive ownership에는 unique_ptr이 맞음
가능하나 더 heavy
Lambda에서 universal reference의 perfect forwarding 방법
auto f = [](auto&& param) {
return func(normalize(std::forward<decltype(param)>(param)));
};
capture
capture는 기본적으로 local variable에 대해서만 가능함
지역 변수의 scope out 시 잘못된 참조 가능
'Programming > JAVA, C++, Go, Rust' 카테고리의 다른 글
C++ file path (파일 경로) 획득 방법 (0) | 2022.10.04 |
---|---|
task-based programming (0) | 2022.04.21 |
Effective Modern C++ (0) | 2022.04.21 |
Universal reference (0) | 2022.04.16 |
make_shared, make_unique의 장점 (0) | 2022.04.16 |