Notice
Recent Posts
Recent Comments
Link
- 책_곽용재님 홈페이지
- 책_노란북 - 책 가격비교
- 책_김재우-SICP번역
- 플밍_쏘쓰포지
- 플밍_CodingHorror ?
- 플밍_상킴
- 플밍_김민장님
- GPGStudy
- 플밍_미친감자님
- 플밍_jz
- 플밍_샤방샤방님
- 플밍_글쓰는프로그래머2
- 플밍_키보드후킹
- 사람_재혁
- 사람_kernel0
- 사람_박PD
- 사람_경석형
- 사람_nemo
- 사람_kikiwaka
- 사람_Junios
- 사람_harry
- 사람_어떤 개발자의 금서목록..
- 사람_모기소리
- 사람_낙타한마리
- 사람_redkuma
- 사람_영원의끝
- 사람_민식형
- 도스박스 다음카페
- 플레이웨어즈 - 게임하드웨어벤치마크
- http://puwazaza.com/
- David harvey의 Reading Marx's c…
- 씨네21
- 한겨레_임경선의 이기적인 상담실
- 본격2차대전만화 - 굽시니스트
- 영화_정성일 글모음 페이지
- 영화_영화속이데올로기파악하기
- 음식_생선회
- 죽력고
- 사람_한밀
- 플밍_수까락
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 진삼국무쌍5
- template
- 건강
- 고등학교 사회공부
- 강유원
- 김두식
- 태그가 아깝다
- 일리아스
- BSP
- 소비자고발
- 인문학
- 진중권
- 영화
- Programming
- 삼국지6
- 정신분석
- programming challenges
- 단상
- 유시민
- 게임
- c++
- stl
- 유머
- 정성일
- 책
- 고전강의
- 프로그래밍
- 삼국지
- 노무현
- modernc++
Archives
- Today
- Total
01-07 02:28
lancelot.com
reference counting 본문
- 소멸자를 proteced 로하면 객체를 힙에만 생성할 수 있다.
#include<iostream>
class RefCount
{
int refcnt = 0;
public :
void addRef() { ++refcnt; }
void release()
{
if (--refcnt == 0)
delete this;
}
// 소멸자를 protected로 하면 외부에서 소멸이 불가능해져서
// 1. delete 불가능 (소멸자 호출이 불가능하므로)
// 2. 지역변수 생성 불가 (지역변수가 소멸될때 소멸자를 호출 할 수 없어서) - 객체를 힙에만 생성할 수 있다.
protected:
virtual ~RefCount() { std::cout << "~RefCount" << std::endl; }
};
class Truck : public RefCount
{
public :
~Truck() { std::cout << "~Truck" << std::endl; }
};
int main()
{
Truck* p1 = new Truck;
p1->addRef();
p1->release();
}
- reference counting class 가 virtual function table을 가지고 있으면 메모리를 더 차지하므로 ~RefCount 의 virtual 을 제거하고싶다 - 가상 소멸자 -> CRTP
- CRTP
- Curiously Recurring Template Pattern
- 파생클래스가 템플릿인자를 통해 기반클래스에 자신의 클래스 이름을 전달하는 테크닉
template<typename T>
class RefCount
{
int refcnt = 0;
public :
void addRef() { ++refcnt; }
void release()
{
// 파생클래스를 delete 하지 못하는 이유는 파생클래스 타입을 알수 없기때문
// 따라서 파생클래스를 템플릿 인자로 전달받으면 가능하다
if (--refcnt == 0)
delete static_cast<T*>(this);
}
// 소멸자를 protected로 하면 외부에서 소멸이 불가능해져서
// 1. delete 불가능 (소멸자 호출이 불가능하므로)
// 2. 지역변수 생성 불가 (지역변수가 소멸될때 소멸자를 호출 할 수 없어서) - 객체를 힙에만 생성할 수 있다.
protected:
~RefCount() { std::cout << "~RefCount" << std::endl; }
};
class Truck : public RefCount<Truck>
{
public :
~Truck() { std::cout << "~Truck" << std::endl; }
};
int main()
{
Truck* p1 = new Truck;
p1->addRef();
p1->release();
}
- const pointer를 생성했을 때도 reference counting 이 작동해야한다
#include<iostream>
// CRTP
// Curiously Recurring Template Pattern
// 파생클래스가 템플릿인자를 통해 기반클래스에 자신의 클래스 이름을 전달
template<typename T>
class RefCount
{
mutable int refcnt = 0; // 상수 멤버함수에서도 수정가능한 멤버로 만들어주는 mutable
public :
void addRef() const { ++refcnt; }
// void release() // void release(RefCount* this)
void release() const // void release(const RefCoutn* this)
{
if (--refcnt == 0)
// delete static_cast<T*>(this); // 캐스팅 error
// delete static_cast<T*>(const_cast<RefCount*>(this));// ok
delete static_cast<const T*>(this); // ok
}
// 소멸자를 protected로 하면 외부에서 소멸이 불가능해져서
// 1. delete 불가능 (소멸자 호출이 불가능하므로)
// 2. 지역변수 생성 불가 (지역변수가 소멸될때 소멸자를 호출 할 수 없어서) - 객체를 힙에만 생성할 수 있다.
protected:
~RefCount() { std::cout << "~RefCount" << std::endl; }
};
class Truck : public RefCount<Truck>
{
public :
~Truck() { std::cout << "~Truck" << std::endl; }
};
int main()
{
const Truck* p1 = new Truck; // p1을 통해서는 읽기만 하겠다.
// 상수 객체는 상수 멤버함수만 호출할 수 있다.
// 상수 객체도 수명은 관리할 수 있어야한다.
p1->addRef();
p1->release();
}
- Truck, Car 등이 계속늘어나면, 중복된 코드메모리가 많이 늘어난다. 이것을 줄일 수 없을까?
- template hoisting
- 템플릿이 동일한 코드를 여러개 만드는 것을 방지
- 템플릿 인자에 의존적이지 않은 멤버는 템플릿이 아닌 기반클래스를 만들어서 별도로 제공
- thin template
#include<iostream>
class RefCountBase
{
protected:
mutable int refcnt = 0; // 상수 멤버함수에서도 수정가능한 멤버로 만들어주는 mutable
public:
void addRef() const { ++refcnt; }
};
template<typename T>
class RefCount : public RefCountBase
{
public :
// void release() // void release(RefCount* this)
void release() const // void release(const RefCoutn* this)
{
if (--refcnt == 0)
// delete static_cast<T*>(this); // 캐스팅 error
// delete static_cast<T*>(const_cast<RefCount*>(this)); // ok
delete static_cast<const T*>(this); // ok
}
};
// Truck, Car 등이 계속늘어나면, 중복된 코드메모리가 많이 늘어난다.
// 이것을 줄일 수 없을까?
// template hoisting
// -> 템플릿이 동일한 코드를 여러개 만드는 것을 방지
// -> 템플릿 인자에 의존적이지 않은 멤버는 템플릿이 아닌 기반클래스를 만들어서 별도로 제공
// -> thin template
class Truck : public RefCount<Truck>{};
class Car : public RefCount<Car> { };
int main()
{
// p1을 통해서는 읽기만 하겠다.
// 상수 객체는 상수 멤버함수만 호출할 수 있다.
// 상수 객체도 수명은 관리할 수 있어야한다.
const Truck* p1 = new Truck;
p1->addRef();
p1->release();
}
- C++11 에 도입된 thread safe 한 atomic 을 적용
#include<iostream>
#include<atomic>
// C++11 에 도입된 threadsafe 한 atomic을 적용.
class RefCountBase
{
protected:
mutable std::atomic<int> refcnt = {0};
public:
void addRef() const
{
// ++refcnt;
refcnt.fetch_add(1, std::memory_order_relaxed);
}
};
template<typename T>
class RefCount : public RefCountBase
{
public :
void release() const // void release(const RefCoutn* this)
{
int ret = refcnt.fetch_sub(1, std::memory_order_acq_rel);// refcnt 에서 1을 빼고, 이전값을 return
if (refcnt == 1)
delete static_cast<const T*>(this);
}
};
int main()
{
const Truck* p1 = new Truck;
p1->addRef();
p1->release();
}
- 포인터가 생성/ 복사 될때마다 수동으로 AddRef를 부르는 것이 힘들다.
- 포인터가 객체라면
- 생성자에서 addref
- 복사 생성자에서 addref
- 소멸자에서 release를 호출해주면, 자동화 가능
#include<iostream>
#include<atomic>
// C++11 에 도입된 threadsafe 한 atomic을 적용.
class RefCountBase
{
protected:
mutable std::atomic<int> refcnt = {0}; // 상수 멤버함수에서도 수정가능한 멤버로 만들어주는 mutable
public:
void addRef() const
{
// ++refcnt;
refcnt.fetch_add(1, std::memory_order_relaxed);
}
};
template<typename T>
class RefCount : public RefCountBase
{
public :
// void release() // void release(RefCount* this)
void release() const // void release(const RefCoutn* this)
{
int ret = refcnt.fetch_sub(1, std::memory_order_acq_rel);// refcnt 에서 1을 빼고, 이전값을 return
if (refcnt == 1)
delete static_cast<const T*>(this);
}
};
class Truck : public RefCount<Truck>
{
public:
~Truck() { std::cout << "~Truck" << std::endl; }
};
template<typename T>
class AutoPtr
{
T* obj;
public:
explicit AutoPtr(T* p = nullptr) : obj(p) { if (obj) obj->addRef(); }
AutoPtr(const AutoPtr<T*>& ap) : obj(ap.obj) { if (obj) obj->addRef(); }
~AutoPtr() { if (obj) obj->release(); }
};
int main()
{
AutoPtr<Truck> p1(new Truck);
AutoPtr<Truck> p2 = p1;
// 매번 생성시 addref 필요없어질시 relase 를 부르는것이 비효율적이다.
// pointer가 객체라면, 생성자/ 복사생성자에서 addref / 소멸자에서 release를 해줄 수있다.
Truck* p3 = new Truck;
p3->addRef();
Truck* p4 = p3;
p4->addRef();
p4->release();
p3->release();
}
- std::shared_ptr 의 단점
- 참조계수를 관리하는 객체가 2개 이상 생성될 수 있는 위험이 있다.
- 객체안에 참조계수(관리객체)가 포함되는 것이 안전하다(std::make_shared)
#include<iostream>
#include<atomic>
#include<memory>
class Truck
{
public:
~Truck() { std::cout << "~Truck" << std::endl; }
};
int main()
{
std::shared_ptr<Truck> sp1 = new Truck;
std::shared_ptr<Truck> sp2 = sp1;
Truck* p1 = new Truck;
std::shared_ptr<Truck> sp3(p1);
std::shared_ptr<Truck> sp4(p1);
}