Click here to Skip to main content
Click here to Skip to main content
Go to top

Safely accessing pointers

, 27 Jan 2013
Rate this:
Please Sign up or sign in to vote.
The topic discusses how to safely access the pointers, which are considered as bomb shells if used unsafely.

Introduction

Accessing a pointer can be problematic in C++ and it requires a lot of good practices to ensure it correct usage. The article discusses how to safely access the pointers using one of the common practice.

Background

Generally pointers are accessed by using the dereference operator. It however can lead to bizarre results if the memory location is not accessible any more.

Using the code

The following piece of code can be used to correctly deal with unsafe pointers. I used templates to create a class ToRef, which need to be instantiated for the type of check on the pointers required, and delete policy (whether it should be deleted after usage).

The code is fairly simple and it simply checks for the pointers before returning the object instead of pointers to use it safely.

I would recommend using r_get() to access the pointer's object and p_get is provided only for tracing and inheritance purposes.

ToRef class

	
	template <typename T_TYPE, typename T_RET, typename T_DELETE_POLICY>
	class ToRef: public std::tr1::tuple<T_TYPE*const&, T_RET, T_DELETE_POLICY>
	{
	public:
		ToRef<T_TYPE, T_RET, T_DELETE_POLICY>(T_TYPE*const& p_type, const T_DELETE_POLICY delete_policy)
			: std::tr1::tuple<T_TYPE*const&, T_RET, T_DELETE_POLICY>(p_type, p_type!=NULL, delete_policy)
		{
		}
	};

	template <typename T_TYPE>
	class ToRef<T_TYPE, bool, bool>: public std::tr1::tuple<T_TYPE*const&, bool, bool>
	{
	public:
		explicit ToRef<T_TYPE, bool, bool>(T_TYPE*const& p_type, const bool delete_policy)
			:std::tr1::tuple<T_TYPE*const&, bool, bool>(p_type, p_type!=NULL, delete_policy)
		{
		}
	
		~ToRef()
		{
			//delete policy
			
			if (std::tr1::get<2>(*this))
			{
				if (NULL != std::tr1::get<0>(*this))
					delete std::tr1::get<0>(*this);

				const_cast<T_TYPE*&>(std::tr1::get<0>(*this))= NULL;
			}
		}

	private:
		ToRef<T_TYPE, bool, bool>(ToRef<T_TYPE, bool, bool>& copy){};
		ToRef<T_TYPE, bool, bool>& operator = (ToRef<T_TYPE, bool, bool>& rhs){};

	public:
		bool is_valid(void) const
		{
			//validity of the pointer.
			return std::tr1::get<1>(*this);
		}
		T_TYPE& r_get(void) const
		{
			if (is_valid())
				return *std::tr1::get<0>(*this);
			throw std::string("Invalid Pointer");
		}

		T_TYPE*const & p_get(void) const
		{
			return std::tr1::get<0>(*this);
		}
	};

	//use it to safely access the pointers.
	//if block is an overhead here.
	#define safe_access_start(Ref) if (Ref.is_valid()) {
	#define safe_access_end } 

	//faster mode but unsafe. exception handling takes care of preventing 
	//unhandled exception to cascade to the top.
	#define fast_access_start try {
	#define fast_access_end } catch (std::string s_exception){ TRACE("%s, %d, %s", __FILE__, __LINE__, s_exception.c_str());}

The pointer validity check can be changed based on the policy of checking the pointers. In that case one need to change the ToRef instantiation and defines the is_valid appropriately.

Using the ToRef template class

The usage is quite simple too. One can use the macros safe_access_start() and safe_access_end to access the pointers. This however has some overhead of checking the pointers everytime one need to access it. The other alternative is to use fast_access_start, fast_access_end, which handles the exception if there is an invalid pointers.

//first test. access only, no delete of memory
	{
		Stest *sTest = new Stest(10);
		//don't delete the memory when done.
		sa::ToRef<Stest, bool, bool> testRef(sTest, false);
	
		//this will safely access the .access() function
		safe_access_start(testRef)
			testRef.r_get().access();
		safe_access_end
	}


	//second test, delete associate memory
	{
		Stest *sTest1 = new Stest(20);
		//delete memory when done.
		sa::ToRef<Stest, bool, bool> testRef(sTest1, true);
		
		//this will safely access the .access() function
		safe_access_start(testRef)
			testRef.r_get().access();
		safe_access_end
	}

	// null pointers. 
	Stest *sTest_1 = NULL;
	{
		sa::ToRef<Stest, bool, bool> testRef(sTest_1, false);
		
		//this will by pass the .access() call
		safe_access_start(testRef)
			testRef.r_get().access();
		safe_access_end
		
		//this will throw an exception
		fast_access_start
			testRef.r_get().access();
		fast_access_end

	}

	//null pointers with delete policy
	{
		sa::ToRef<Stest, bool, bool> testRef(sTest_1, true);
		
		//this will by pass the .access() call
		safe_access_start(testRef)
			testRef.r_get().access();
		safe_access_end
		
		//this will throw an exception
		fast_access_start
			testRef.r_get().access();
		fast_access_end
	}

Using ToNRef template class

The ToNRef template class solves another problem, where one would like to initialize a pointer with default constructor if the pointer is invalid. This can also be combined with valid check and delete policy.

	//use in case you want to initialize a pointer if it is null
	template <typename T_TYPE, typename T_RET, typename T_DELETE_POLICY>
	class ToNRef: public std::tr1::tuple<T_TYPE*&, T_RET, T_DELETE_POLICY>
	{
	public:
		ToNRef<T_TYPE, T_RET, T_DELETE_POLICY>(T_TYPE*& p_type, const T_DELETE_POLICY delete_policy)
			: std::tr1::tuple<T_TYPE*&, T_RET, T_DELETE_POLICY>(p_type, (p_type== NULL? p_type= new T_TYPE(1):p_type)!=NULL, delete_policy)
		{
		}
	};

	template <typename T_TYPE>
	class ToNRef<T_TYPE, bool , bool>: public std::tr1::tuple<T_TYPE*&, bool, bool>
	{

	public:
		ToNRef<T_TYPE, bool, bool>(T_TYPE*& p_type, const bool delete_policy)
			: std::tr1::tuple<T_TYPE*&, bool, bool>(p_type, (p_type== NULL? p_type= new T_TYPE(1):p_type)!=NULL, delete_policy)
		{
		}

		~ToNRef()
		{
			//delete policy
			if (std::tr1::get<2>(*this))
			{
				if (NULL != std::tr1::get<0>(*this))
					delete std::tr1::get<0>(*this);

				const_cast<T_TYPE*&>(std::tr1::get<0>(*this))= NULL;
			}
		}

	private:
		ToNRef<T_TYPE, bool, bool>(ToNRef<T_TYPE, bool, bool>& copy){};
		ToNRef<T_TYPE, bool, bool>& operator = (ToNRef<T_TYPE, bool, bool>& rhs){};

	public:
		bool is_valid(void) const
		{
			//validity of the pointer.
			return std::tr1::get<1>(*this);
		}

		T_TYPE& r_get(void) const
		{
			if (is_valid())
				return *std::tr1::get<0>(*this);
			throw std::string("Invalid Pointer");
		}
		
		T_TYPE*const & p_get(void) const
		{
			return std::tr1::get<0>();
		}
	};

Using the ToNRef class

//null pointers with reinitialization.
	{
		sa::ToNRef<Stest, bool, bool> testRef(sTest_1, true);
		
		////this will by pass the .access() call
		safe_access_start(testRef)
			testRef.r_get().access();
		safe_access_end
		
		//this will throw an exception
		fast_access_start
			testRef.r_get().access();
		fast_access_end
	}

Restrictions

It however has some shortcomings. The code is not thread-safe so one would have to explicitly takes care of atomicity if the code is intended for use in multi-threading environment. The code with deletion policy is not meant to be used with the shared pointers.

Copying of the ToRef and ToNRef is prevented by making copy constructor and assignment operator private.

License

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

Share

About the Author

Jaskiran Singh
Architect
United States United States
I have been designing and developing for more than 10 years and c++ is my passion that continue to this day. I like writing more and more generic code and simple modular code that could be easily reused.

Comments and Discussions

 
GeneralMy vote of 3 PinmemberBartlomiej Filipek24-Jun-14 20:36 
QuestionBetter alternative - use std::tr1::weak_ptr or boost::weak_ptr Pinmembersteveb30-Jan-13 15:23 
GeneralMy vote of 4 PinmemberDavid Serrano Martínez27-Jan-13 1:29 
GeneralMy vote of 5 Pinmembergagankaur21-Jan-13 8:36 
GeneralExcellent solution Pinmembergagankaur21-Jan-13 8:33 
GeneralMy vote of 5 PinmvpMichael Haephrati מיכאל האפרתי8-Jan-13 10:28 
GeneralMy vote of 1 PinmemberMichaelgor8-Jan-13 8:42 

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

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

| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 27 Jan 2013
Article Copyright 2013 by Jaskiran Singh
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid