Click here to Skip to main content
13,248,766 members (58,417 online)
Click here to Skip to main content
Add your own
alternative version


2 bookmarked
Posted 3 Jun 2012

Ideas from a smart pointer(part 2). size == sizeof(std::shared_ptr)/2

, 3 Jun 2012
Rate this:
Please Sign up or sign in to vote.
embedded ref-counted resource management


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

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 more quickly than std::shared_ptr or boost::shared_array.


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

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 scenarios. For C++ build-in data types, it is impossible to have inheritance. This limitation offers us an opportunity to minimized the smart pointer.

Memory distribution for build-in object allocation

ref-counter blank area to ensure build-in object's alignment build-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

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; 
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. build-in types can ignore
        return NativeCore<T, align>(reinterpret_cast<T*>(p+1), align);
        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);
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 build-in types. No longer need explicit free or delete.

#include "stdafx.h" 
int main() 
    using namespace std;

    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 build-in types) and the ref-counter can share the same one heap, we will produce more economical smart pointer than the     shared_ptr<vector> or boost::shared_array We will do the corresponding test in the part 3.


Created by Wei, Bing in 2006


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


About the Author

China China
No Biography provided

You may also be interested in...

Comments and Discussions

GeneralMy vote of 3 Pin
Emilio Garavaglia4-Jun-12 10:04
memberEmilio Garavaglia4-Jun-12 10:04 
GeneralRe: My vote of 3 Pin
weibing4-Jun-12 21:39
memberweibing4-Jun-12 21:39 
Questionsizeof(boost::shared_ptr<>) == 8 Pin
valdok3-Jun-12 7:17
membervaldok3-Jun-12 7:17 
The reason why shared_ptr<> has a size of 2 integral types is different. It's designed as a non-intrusive smart pointer. I.e. the object itself doesn't implement reference counting. The shared_ptr<> consists of two things:

1. An opaque pointer to the specified type.
2. A pointer to an auxiliary data structure that includes the following:
2a. The reference count (plus so-called weak reference count).
2b. A pointer to a factory that knows how to destroy the object.

The drawback of such a smart pointer design is increased size (as you've noticed). Plus - worse performance. When the object is assigned to it via reset method - it allocates its auxiliary data, hence there're two allocations. This can be solved by creating an object via make_shared. However still its performance is somewhat inferior, due to a use of factory which imposes vcalls, worse optimization depth and etc. Plus - one may not mix both shared and raw pointers. I.e. you may not extract a pointer from the smart pointer, pass it as a parameter, and there re-wrap it with another smart pointer (which is absolutely legal with intrusive smart pointers).

OTOH shared_ptr has several advantages. It imposes minimum dependency: you can freely use a shared_ptr of incomplete types. The only place where the type should be well-defined is the assignment of the shared_ptr.
Also it words correctly with polymorphic type, even without virtual d'tor.
AnswerRe: sizeof(boost::shared_ptr) == 8 Pin
weibing4-Jun-12 0:47
memberweibing4-Jun-12 0:47 
GeneralRe: sizeof(boost::shared_ptr) == 8 Pin
Dendrobates4-Jun-12 11:35
memberDendrobates4-Jun-12 11:35 
GeneralRe: sizeof(boost::shared_ptr) == 8 Pin
weibing4-Jun-12 21:26
memberweibing4-Jun-12 21: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.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.171114.1 | Last Updated 3 Jun 2012
Article Copyright 2012 by weibing
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid