Martin's Tex-Blog

Posts on programming and generally technical topics

Archive for November 2014

shared_ptr and aliasing constructor

leave a comment »


shared_ptr seems to be straightforward class, but if you look closer you will find aliasing constructor that make you think – what the hell is that?

template<class Y>
shared_ptr( const shared_ptr<Y>& r, T *ptr );

The idea of this constructor is to keep reference count of owned object – r, while using pointer of some other stored object (mostly field in r object). This means that shared_ptr is allowed to own one object and store some other raw pointer. Only owned object is reference counted, while stored pointer is the one that gets returned during dereferencing. In normal situation, when aliasing constructor is not used, raw pointer is the one for owned object.

Below is example of aliasing constructor usage, where stored pointer is contained inside owned one:

#include <iostream>
#include <memory>

struct Data {
	int d;
};

int main(int argc, char* argv[])
{
	std::shared_ptr<Data> pd(new Data());
	std::shared_ptr<int> pd_d(pd, &pd->d); //pd is owned object, while &pd->d is stored (retuned on dereference)

	*pd_d = 10; 

	std::cout << pd.use_count() << std::endl; // outputs 2
	pd_d.reset();
	std::cout << pd.use_count() << std::endl; // outputs 1
	pd.reset();
	std::cout << pd.use_count() << std::endl; // outputs 0

	// pd->d is now a dangling pointer, dont use it!

	return 0;
}

Other use is for making shared_ptr of derived type:

        struct Base {
	  virtual ~Base(){}
        };
        struct Derived : public Base {
	  void foo() {}
        };

	std::shared_ptr<Base> pd_base(new Derived());
	std::shared_ptr<Derived> pd_derived(pd_base, dynamic_cast<Derived*>(pd_base.get()));
	pd_derived->foo();

Other use case for aliasing constructor is when you are using interface that requires shared_ptr but you dont want it to keep ownership of your object. In such case you can make shared_ptr to work like normal bare pointer:

    static Data globalData;
    std::shared_ptr pd2(std::shared_ptr(), &globalData.d);
    std::cout << pd2.use_count() << std::endl; // outputs 0, but *pd is valid
    *pd2 = 2;

Aliasing constructor is not forcing you to keep pointer as a stored object, it can be any type of size_t. When you realize this you can use this constructor also for keeping timestamp in some caching mechanism. For further info look here:

http://www.codesynthesis.com/~boris/blog/2012/04/25/shared-ptr-aliasing-constructor/

Other uses are: refering to container element (owned object is container, stored pointer is element in container), referring to a shared library symbol.

What you should not do:

	std::shared_ptr pd(new Data());
	std::shared_ptr pd_d(pd, new int()); // here new int() is an error, this pointer will never be freed.
Advertisements

Written by Marcin

November 15, 2014 at 5:19 pm

Posted in C++