Click here to Skip to main content
15,868,016 members
Articles / Programming Languages / Visual C++ 10.0

Ideas from a Smart Pointer(part 2). size == sizeof(std::shared_ptr)/2

Rate me:
Please Sign up or sign in to vote.
3.50/5 (2 votes)
3 Jun 2012CPOL1 min read 27.5K   103   2   6
Embedded ref-counted resource management

Introduction

For prototype with no inheritance such as char, short, int, float..., we can further optimize to make sizeof (smart pointer) == sizeof (raw pointer).

Examples in Win32:

C++
sizeof(int*) == 4
sizeof(std::shared_ptr<int>) == 8
sizeof(boost::shared_array<int>) == 8
sizeof(smart pointer in this section) == 4

It is more economical and quicker than std::shared_ptr or boost::shared_array.

Background

In C++, the inheritance relationship will change the pointer value if we do the pointer conversion. Try the following simple code:

C++
struct base
{
};

struct der: base
{
	virtual void fff()
	{
	}
};

int main()
{
    der obj;
    base* p = &obj;

    if(p == &obj)
    {
        printf("pointer is equal\n");
    }

    if(unsigned(p) != (unsigned)&obj)
    {
	printf("value is not equal\n");
    }
}

The program output is "pointer is equal" and "value is not equal".

That's why the sizeof(std::shared_ptr<>) == 8 in the Win32 system. It can't retrieve the reference counting position from the object pointer value caused by inheritance. So it needs to store an additional pointer inside.

The Solution

There is one exception scenario. For C++ built-in data types, it is impossible to have inheritance. This limitation offers us an opportunity to minimized the smart pointer.

Memory distribution for built-in object allocation:

ref-counter blank area to ensure build-in object's alignment built-in object

Memory distribution for build-in array objects allocation:

ref-counter blank area to ensure build-in object's alignment build-in object1 build-in object2 ...

We directly use the overloaded new operator to embed the reference counter in the object heap during allocation. The reference counter and the object(s) share the same heap, which will greatly enhance efficiency and reduce resource consumption. Think about boost::make_shared.

Some key code:

C++
template<typename T, size_t align>
struct NativeCore
{
    NativeCore(T* p, size_t align): m_ptr(p), m_align(align) { }
    T* m_ptr; size_t m_align; 
};
C++
template<typename T, void*(*allocator)(size_t), void(*cleaner)(void*), size_t align> 
inline NativeCore<T, align> NativeAllocMemOnly(size_t total)
{ 
    size_t remain = sizeof(long)%align;
    if(0 == remain)
    {
        long* p = reinterpret_cast<long*>(allocator(sizeof(T)*total + sizeof(long)));
        *p = 1;
        ::new(cleaner, p+1) T[total]; //constructor for extended uses. 
                                      //built-in types can ignore
        return NativeCore<T, align>(reinterpret_cast<T*>(p+1), align);
    }
    else
    {
        char* p = reinterpret_cast<char*>(allocator(sizeof(T)*total + 
                              sizeof(long) + align - remain));
        *reinterpret_cast<long*>(p) = 1; ::new(cleaner, p+sizeof(long) + 
         align - remain) T[total]; //constructor for extended uses. build-in types can ignore
        return NativeCore<T, align>(reinterpret_cast<T*>(p+sizeof(long) + 
                                    align - remain), align);
    }
}
C++
template<typename T, size_t alignment>
inline NativeCore<T, alignment> NativeAlloc(size_t total)
{
    StaticAssert<!IsClassType<T>::m_value>(); //no class type
    return NativeAllocMemOnly<T, operator new, operator delete, alignment>(total);
}

Test code
Ref-counted management for built-in types. No longer need explicit free or delete.

C++
#include "stdafx.h" 
int main() 
{
    using namespace std;
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);

    RefNative<int>::Normal n = NativeAlloc<int>(2); //two int
    n[0] = 1;
    n[1] = 2;
    int* pn = n;

    printf("Current ref-count before push_back = %d\n", n.GetRefCount());
    std::vector<RefNative<int>::Normal> vec;
    vec.push_back(n);    //auto InterlockedIncrement to make thread-safe
    printf("Current ref-count after push_back = %d\n", n.GetRefCount());

    printf("sizeof(RefNative<int>::Normal) = %d\n", sizeof(RefNative<int>::Normal));

    //demo aligned operation
    RefNative<double, 64>::
    Aligned na = NativeAllocAligned<double, 64>(1); //allocate one 64 bytes aligned double
    double* pna = na; pna[0] = 3.5;
    printf("sizeof(RefNative<int>::Aligned) = %d\n", sizeof(RefNative<int>::Aligned));
    if(unsigned(pna)%64 == 0)
    {
       puts("it is properly 64 bytes aligned\n");
    }
    return 0; 
}

Points of Interest

If the array objects (not just built-in types) and the ref-counter can share the same one heap, we will produce a more economical smart pointer than the shared_ptr<vector> or boost::shared_array.
We will do the corresponding test in Part 3.

History

  • 2006: Created
  • 3rd June, 2012: Article posted

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
China China
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionsizeof(boost::shared_ptr<>) == 8 Pin
valdok3-Jun-12 6:17
valdok3-Jun-12 6:17 
AnswerRe: sizeof(boost::shared_ptr) == 8 Pin
weibing3-Jun-12 23:47
weibing3-Jun-12 23:47 
GeneralRe: sizeof(boost::shared_ptr) == 8 Pin
Ranophile4-Jun-12 10:35
professionalRanophile4-Jun-12 10:35 
GeneralRe: sizeof(boost::shared_ptr) == 8 Pin
weibing4-Jun-12 20:26
weibing4-Jun-12 20:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.