웹키 스마트 포인터 - RefPtr, PassRefPtr

6978 단어

역사.


2005년까지 웹키트의 많은 대상들이 인용 계수 방식을 채택했다.이러한 모드는 RefCounted 클래스 템플릿을 상속하여 수행됩니다.RefCounted는 주로 ref()와deref() 두 함수를 실현했다.인용 대상이 필요할 때ref()를 호출하여 인용 계수를 늘리고, 대상이 더 이상 필요하지 않을 때deref() 함수를 호출하여 인용 계수를 줄인다.ref ()와deref () 는 쌍으로 나타나야 합니다.이것은 new/delete를 사용하는 것과 마찬가지로, 다중 호출, 적은 호출, 호출되지 않은 문제가 항상 자주 발생한다.만약 컴파일러가 자동으로ref,deref의 호출을 완성할 수 있다면 C/C++프로그래밍의 버그는 적어도 절반 이상 줄일 수 있다.ps: C/C++ 프로그래밍의 대부분 문제는 메모리 문제로 나타나지만 사실 근원은 논리와 절차 문제일 수 있습니다.그나저나 컴파일러가 메모리를 자동으로 관리할 수 있다면 프로그래머의 부담을 크게 줄일 수 있을 것이다.
RefCounted 의 주요 함수는 다음과 같습니다.
void ref()
{
	++m_refCount;
}
// Returns whether the pointer should be freed or not.
bool derefBase()
{
	unsigned tempRefCount = m_refCount - 1;
	if (!tempRefCount) {
		return true;
	}
	m_refCount = tempRefCount;
	return false;
}

void deref()
{
	if (derefBase())
		delete static_cast<T*>(this);
}

RefPtr


RefPtr는ref()와deref() 구성원 함수를 가진 모든 대상에 적용될 수 있으며, 대상의ref()와deref()를 자동으로 호출하여 바늘의 스마트 관리를 실현할 수 있다.Webkit의 많은 대상들이 RefCounted를 계승하여 인용 계수 모드(ref()와deref() 함수를 가지고 있음)를 실현한다.
RefPtr는 값을 전송할 때ref()를 자동으로 호출하여 인용 계수를 증가시키고, 값을 전송할 때deref()를 자동으로 호출하여 인용 계수를 감소시킵니다.deref () 를 호출할 때 인용 계수가 1이면 해당 대상을 삭제한다는 것을 알고 있습니다.
RefPtr는 바늘을 관리하는 데 쓰인다. 즉, 바늘이 먼저 있어야 한다. 바늘이 어떻게 생겼는지, 아무리 변해도 그 안에서 벗어나지 않으면 new가 나와야 한다.new에서 나온 우리는 누드 포인터라고 부른다. 이른바 RefPtr 관리에 맡기는 것은 new에서 나온 누드 포인터를 RefPtr 대상에게 전달하는 것이다.지능 지침 내부는 적당한 시기에 delete가 관리하는 누드 지침에 맡긴다.
adoptRef () 를 통해 누드 포인터를 RefPtr에 값을 부여합니다.언제든지 new 대상을 호출할 때adoptRef를 즉시 호출해야 합니다.deref () 호출을 잊어버리지 않습니다.
RefPtr<Foo> foo = adoptRef(new Foo());

효율적인 고려에서 Foo는 창설할 때 인용 계수를 1로 부여하기 때문에 new의 대상을 RefPtr에 직접 부여할 수 없습니다. 왜냐하면 그렇게 하면 new의 대상을 영원히 방출할 수 없기 때문입니다.그래서 소유권을 이전하기 위해adoptRef를 사용해야 한다.
RefCountedBase()
	: m_refCount(1)
{
}

adoptRef의 구현(PassRefPtr.h):
template<typename T> inline PassRefPtr<T> adoptRef(T* p)
{
    adopted(p);
    return PassRefPtr<T>(p, PassRefPtr<T>::Adopt);
}

여기서 adopted는 실제로 아무것도 하지 않았습니다. 정의는 다음과 같습니다(PassRef.h).
inline void adopted(const void*) { }

다음에 호출되는 PassRefPtr 정의는 다음과 같습니다.
enum AdoptTag { Adopt };
PassRefPtr(T* ptr, AdoptTag) : m_ptr(ptr) { }

이 함수는 매개변수에 대한 기본 포인터를 사용하여 PassRefPtr 임시 객체를 생성합니다.위의 예에서는 실제 값이 RefPtr 객체에 지정되어 다음 변환 함수를 사용합니다.
template<typename T> template<typename U> inline RefPtr<T>::RefPtr(const PassRefPtr<U>& o)
        : m_ptr(o.leakRef())
 { }
template<typename T> inline T* PassRefPtr<T>::leakRef() const
{
    T* ptr = m_ptr;
    m_ptr = nullptr;
    return ptr;
}

leakRef ()PassRefPtr를 누드 포인터로 옮깁니다.
한 마디로 하면adoptRef는 RefCounted에서 계승된 대상을 RefPtr 관리에 맡기는 것이다.

RefPtr의 단점


다음 예를 보도록 하겠습니다.
// example, not preferred style; should use RefCountedand adoptRef (see below)
 
RefPtr<Node> createSpecialNode()
{
      RefPtr<Node> a = new Node;
       a->setSpecial(true );
        return a ;
}
 
RefPtr<Node> b = createSpecialNode();

토론을 편리하게 하기 위해서, 우리는 노드 대상이 시작할 때 인용 계수가 0이라고 가정한다.이것은 a에 값을 부여받았을 때 인용 계수가 1로 증가합니다.반환 값을 만들 때 인용 계수가 2로 증가하고 a가 소각되면 인용 계수가 감소하여 1이 됩니다.b를 만들 때 인용 계수가 2로 증가한 다음에createSpecialNode의 반환 값이 삭제되고 인용 계수가 1로 감소합니다.
컴파일러가 반환값 최적화를 실현하면 인용 계수가 증가하고 감소하는 횟수가 감소할 수 있습니다.만약 매개 변수와 반환 값이 모두 스마트 지침이라면 인용 계수가 덧붙인 비용이 더욱 클 것이다.이 문제를 해결하는 방법은 PassRefPtr를 사용하는 것입니다.

PassPtrRef


패스 RefPtr와 RefPtr는 조금 다르다. 패스 RefPtr를 복사하거나 패스 RefPtr의 값을 하나의 RefPtr 또는 다른 패스 RefPtr에 부여할 때 원래의 바늘 값이 0으로 설정된다.이 동작은 인용 계수의 값을 바꾸지 않습니다.
패스워드와 관련된 PassPtrRef의 구현을 살펴보겠습니다.
PassRefPtr(const PassRefPtr& o) : m_ptr(o.leakRef()){ }
template<typename U> PassRefPtr(constPassRefPtr<U>& o) : m_ptr(o.leakRef()) { }
template<typename T> inline T*PassRefPtr<T>::leakRef() const
{
    T* ptr = m_ptr;
    m_ptr =nullptr;
    return ptr;
}

leakRef는 관리 바늘을 값의 수신자에게 옮기는 것으로 인용 계수와 관련이 없는 조작이다.오른쪽 값인 PassRefPtr 객체는 더 이상 사용할 수 없다는 점에 유의하십시오.따라서 함수 매개 변수와 반환 유형에만 PassRefPtr를 사용해야 합니다.
PassRefPtr의 존재는 매개 변수가 전달되고 함수가 되돌아올 때 RefPtr를 사용해서 발생하는 인용 계수 조작을 줄이기 위한 것이다.

PassRefPtr 및 RefPtr


하나의 PassRefPtr로 RefPtr를 초기화하거나 RefPtr에 값을 부여한 후 원래의 PassRefPtr를 사용할 수 없습니다
template<typename T> template<typename U>inline RefPtr<T>::RefPtr(const PassRefPtr<U>& o)
        :m_ptr(o.leakRef())
{
}

하나의 RefPtr로 PassRefPtr를 초기화할 때 인용 계수 동작과 관련이 없습니다.
 template<typename T> template<typename U> inlinePassRefPtr<T>::PassRefPtr(const RefPtr<U>& o)
        :m_ptr(o.get())
{
    T* ptr = m_ptr;
   refIfNotNull(ptr);
}

따라서 PassRefPtr가 사용되기 전에 RefPtr 관리 포인터를 놓지 않도록 해야 합니다.다음과 같은 사용 원칙을 따르면 문제가 되지 않는다.
PassRefPtr는 함수 매개 변수와 반환 유형에서만 사용되며, 함수 매개 변수를 RefPtr로 복사해서 사용합니다.
 

기본 포인터 및 RefPtr, PassRefPtr


그것들 사이는 서로 전환할 수 있지만 실제로는 그럴 필요가 없다. 누드 포인터를 사용한 것처럼 RefPtr,PassRefPtr를 직접 사용할 수 있다. 왜냐하면 조작부호'*','->'가 다시 불러왔기 때문이다.
T& operator*() const { return *m_ptr; }
ALWAYS_INLINE T* operator->() const { return m_ptr; }

사용 설명서


로컬 변수


소유권과 생명 주기를 확정할 수 있다면 로컬 변수는 누드 포인터일 수 있습니다.
만약 확정할 수 없고 소유권이나 성명 주기가 필요하다면 RefPtr를 사용해야 한다.
로컬 변수는 PassRefPtr가 아니어야 합니다.

데이터 멤버


소유권과 생명 주기를 확정할 수 있다면 데이터 구성원은 누드 포인터가 될 수 있다.
만약 확정할 수 없고 소유권이나 성명 주기가 필요하다면 RefPtr를 사용해야 한다.
데이터 구성원은 PassRefPtr가 아니어야 합니다.

함수 매개 변수


만약 함수가 대상을 차지하지 않는다면, 반드시 누드 포인터를 매개 변수로 사용해야 한다.
함수가 객체를 소유해야 하는 경우 PassRefPtr를 사용해야 합니다.대부분의setter 함수는 이렇다.매개 변수는 함수의 사용이 매우 간단하지 않으면 함수가 시작할 때 RefPtr에 전달되어야 한다."prp"접두어를 사용하여 매개변수의 이름을 지정할 수 있습니다.

함수 반환값


만약 함수가 대상을 되돌려주지만 소유권을 옮기지 않는다면, 되돌려주는 값은 누드 바늘이어야 한다.예를 들어 대부분의 Getter 함수.
함수 반환 값이 new 대상이거나 소유권을 이전해야 한다면 반환 값은PassRefPtr를 사용해야 합니다.로컬 변수는 보통 RefPtr이기 때문에 되돌아오는 문장에서release를 자주 호출해서 RefPtr를PassRefPtr로 옮깁니다.
PassRefPtr<T> release() { PassRefPtr<T> tmp =adoptRef(m_ptr); m_ptr = nullptr; return tmp; }

새 대상


언제든지 new 대상은 스마트 포인터가 모든 인용 계수 작업을 자동으로 완성할 수 있도록 RefPtr에 즉시 넣어야 합니다.
RefCounted 객체의 경우 adoptRef 함수를 사용하여 이러한 작업을 수행해야 합니다.
RefCounted 객체가 아닌 경우 private 구조 함수와 PassRefPtr를 반환하는public create 함수를 사용하는 것이 최선의 방법입니다.
class Item {
public:
    PassRefPtr<Item>CreateItem() { }
private:
    Item(){}
};
 
PassRefPtr<Item> CreateItem()
{
    RefPtr<Item>a = new Item;
    return a.release();
}
 

참조:http://blog.csdn.net/wy5761/article/details/20654275

좋은 웹페이지 즐겨찾기