LipingPtr C++ Template Class






4.04/5 (12 votes)
A C++ Smart Pointer Template Class Implementation
Introduction
LipingPtr
is a reference counted C++ Smart Pointer Template Class. It keeps track of the pointer usage and deletes it when the reference counter reaches 0
. LipingPtr
tried to combine most frequently used features of smart pointers and represent them in a single file. Keeping the usage simple is another goal of LipingPtr
. It may not include all the functionalities smart pointers could have; but it tries hard to avoid using confusing terminologies.
1.1 Design Principle
High performance and easy to use are the major design considerations for LipingPtr
.
1.2 Thread-safe
LipingPtr
uses “Critical Section” in Win32 and Win64 for the shared data protection. It uses pthread_mutex_t
for the protection in Linux.
1.3 Support Custom De-Allocator
By default, LipingPtr
uses delete
to release the allocated memory. The user can also define his or her own de-allocator to release the resources.
1.4 LipingArray
LipingArray
is a class that derived from LipingPtr
. It is designed for managing multi dimensional arrays. It can also save and load array content to/from files. LipingArray
issues delete []
to release allocated memory at the end.
1.5 BinData
BinData
is a predefined data type which uses LipingArray
to manage binary data.
1.6 FilePtr
It is a predefined data type which uses LipingPtr
to keep track of FILE pointer. It is also used in LipingArray SaveFile()
and LoadFile()
. FilePtr
closes the opened file(s) when the FilePtr
goes out of scope.
2. Build LipingPtrTest Demo Project
2.1 Visual Studio 2005 Build
LipingPtrTest.sln is the solution file for Visual Studio 2005. You can select the build for Win32 or Win64 platforms.
2.2 Linux Build
You can use the makefile to build LipingPtrTest
on Linux.
3. Basic Usage
3.1 Manage Object Pointer
Create a new class instance and use LipingPtr
to manage it.
Sample Code
{
LipingPtr<LipingPtrTest> p1(new LipingPtrTest);
p1->Hello(true);
}
The LipingPtrTest
instance p1
will be released when it is out of the code section.
Note: LipingPtrTest
definition is attached at the end of this file.
3.2 Manage Object Array Pointer
3.2.1 Manage LipingPtrTest array by using LipingArray
This is the sample code that creates an array of LipingPtrTest
and calls the Hello
method for each array element.
{
int arraySize = 3;
LipingArray<LipingPtrTest> cp(new LipingPtrTest[arraySize]);
for (int i = 0; i < arraySize; i++)
{
if (printit) cp.RawPtr()[i].Hello(printit);
}
}
3.2.2 Manage char array by using LipingArray
{
const int arraySize = 1024*10;
LipingArray<char> cp(new char[arraySize]);
strcpy_s(cp.RawPtr(), arraySize, "Hello LipingArray");
if (printit) printf("LipingPtr<char> = %s\n", cp.RawPtr());
}
3.2.3 Manage unsigned char array by BinData
This sample shows how to use BinData
to manage the unsigned char
array and Save/Load data to/from files.
{
const int arraySize = 256;
BinData bin(arraySize);
unsigned char* p = bin.RawPtr();
for (int i = 0; i < arraySize; i++)
{
p[i] = (unsigned char) i%255;
}
bin.SaveFile("binData.bin");
bin.ReleaseData();
BinData tbin;
tbin.LoadFile("binData.bin");
}
3.2.4 Explicitly Use De-Allocator
This sample code shows how to define and use a de-allocator (CppFree
) to release the memory allocated by malloc.
template<class T>
class CppFree
{
public:
static void Free(T *p)
{
LIPING_PTR_DEBUG_TRACE(p);
if (p) free((void*)p);
}
};
{
const int arraySize = 1024*10;
LipingPtr<char, CppFree<char> >
cp((char*)malloc(sizeof(char)*arraySize));
strcpy(cp.RawPtr(), "Hello LipingArray");
PrintCp(cp.RawPtr(), printit);
}
3.2.5 Use LipingPtr with STL Vector
This sample creates LipingPtr
object and puts it in STL vector. The LipingPtr
managed memory will be released when the vector (tvector
) goes out of scope.
{
LipingPtr<LipingPtrTest> p1(new LipingPtrTest);
std::vector< LipingPtr<LipingPtrTest> > tvecotr;
for(int i=0; i<3; i++)
{
tvecotr.push_back(
LipingPtr<LipingPtrTest>(new LipingPtrTest()) );
}
for(unsigned int i=0; i<tvecotr.size(); i++)
{
tvecotr[i]->Hello(printit);
}
}
3.2.6 Use LipingPtr with STL Map
a. Add map element by string ID
{
typedef LipingPtr<LipingPtrTest> TestPtr;
map< string, TestPtr > smap;
smap["hello-1"] = TestPtr(new LipingPtrTest());
smap["hello-2"] = TestPtr(new LipingPtrTest());
smap["hello-3"] = TestPtr(new LipingPtrTest());
map< string, TestPtr >::iterator it;
for(it = smap.begin(); it != smap.end(); it++)
{
if (printit) printf("Index string = %s; ",
(*it).first.c_str());
(*it).second->Hello(printit);
}
}
b. Different Ways to Add Items in Map
if (printit) printf("Different ways for adding map items:\n");
{
map< int, TestPtr > imap;
if (printit) printf(" << Two step add:\n");
TestPtr inputPtr = new LipingPtrTest();
imap[111] = inputPtr;
if (printit) printf(" >> End two step add\n");
if (printit) printf("One step add:\n");
imap[121] = TestPtr(new LipingPtrTest());
if (printit) printf("end - One step add\n");
if (printit) printf("Two step add by pair:\n");
PtrPair x(131, new LipingPtrTest());
imap.insert(x);
if (printit) printf("end - Two step add by pair\n");
if (printit) printf("One step add by pair:\n");
imap.insert(PtrPair(141, new LipingPtrTest()));
if (printit) printf("end - One step add by pair\n");
}
c. Put LipingPtr Object as First Map Element
The managed object (LipingPtrTest
) must supports “<
” operator in order to put the LipingPtr
instance in STL map.
{
typedef LipingPtr<LipingPtrTest> TestPtr;
typedef pair<TestPtr, int> SiPair;
map< TestPtr, int > imap;
imap.insert(SiPair(new LipingPtrTest(), 141));
imap.insert(SiPair(new LipingPtrTest(), 142));
imap.insert(SiPair(new LipingPtrTest(), 143));
map< TestPtr, int >::iterator it;
for(it = imap.begin(); it != imap.end(); it++)
{
if (printit) printf("Int value = %d; ", (*it).second);
(*it).first->Hello(printit);
}
}
d. Use LipingPtr with STL Vector and Sort Algorism
This sample code puts LipingPtr
objects into STL vector and sorts them by STL sort method.
{
const int arraySize = 10;
typedef LipingPtr<LipingPtrTest> LipingTestPtr;
typedef vector<LipingTestPtr> VTest;
VTest ta;
if (printit) printf("\n");
if (printit) printf("Print random items:\n");
for (unsigned int i = 0; i < arraySize; i++)
{
LipingTestPtr ptr(new LipingPtrTest);
ptr->index = (int)(rand());
ptr->Hello(printit);
ta.push_back(ptr);
}
std::sort(ta.begin(), ta.end());
VTest::iterator it;
if (printit) printf("\n");
if (printit) printf("Print sorted items:\n");
for (it = ta.begin(); it != ta.end(); it++)
{
(*it)->Hello(printit);
}
if (printit) printf("\n");
}
3.3 Attach, Detach, and ReleaseData Functions
LipingPtr
and LipingArray
provide Attach
, Detach
, and ReleaseData
functions to directly access the hidden raw pointer. These actions will affect all LipingPtr
or LipingArray
objects that are referring to the same raw pointer. It is not recommended to use these functions often if you can use other functions to complete the task.
Here is a sample code that shows the proper usage for Attach
and Detach
:
{
int arraySize = 100;
int *a = new int[arraySize]; // allocate a new array
LipingArray<int> ia(a, arraySize); // initialize ia with allocated array
for (unsigned int i = 0; i < ia.Size(); i++) // assign values for each element
{
ia[i] = i;
}
{
int *b = ia.Detach(); // detach the array from ia
LipingArray<int> ib(b, arraySize); // let ib manage the array pointer
for (int i = 0; i< arraySize; i++) // change the array element values
{
ib[i] = i*3;
}
ia.Attach(ib.Detach(), arraySize);
// detach the array from ib and let ia manage the pointer
LipingArray<int> aa(new int[arraySize], arraySize); // create a new array
for (int i = 0; i< arraySize; i++) // assign value to aa elements
{
aa[i] = i;
}
LipingArray<int> ic = ia;
ia.Attach(aa.Detach(), arraySize);
// let ia manage aa's array pointer.
// the old array pointer that ia manages will be released
// before attach aa's pointer.
}
}
This code section shows the complex Attach
, Detach
, InitData
, SaveFile
, and LoadFile
functions usage:
{
LipingArray<long> xx;
{
unsigned int arraySize = 100;
// Allocate the memory by arraySize
LipingArray<long> a(arraySize);
// Get the array arraySize by Size
for (unsigned int i = 0; i < a.Size(); i++)
{
// Access the element by [] operator
a[i] = 'a' + i%('z'-'a'+1);
}
// Direct access the buffer
memset(a.RawPtr(), 0, a.Size()*sizeof(long));
long *lx = new long[arraySize];
// Attach a pre-allocated memory - the old memory is released
a.Attach(lx, arraySize);
// Attach a newly allocated array - the old memory is released
a.Attach(new long[arraySize], arraySize);
LipingArray<long> aa;
aa = a;
LipingArray<long> bb(aa);
// Detach the allocated memory *** Careful to use!!! Advanced usage!!!
lx = a.Detach();
// Release the detached memory
delete [] lx;
LipingArray<long> cc(arraySize);
// Get the array arraySize by Size
for (unsigned int i = 0; i < cc.Size(); i++)
{
// Access the element by [] operator
cc[i] = 'a' + i%('z'-'a'+1);
}
// Add another reference; the memory will be kept even cc is released
xx = cc;
}
// Release the old memory got from cc; and track a new array
xx = LipingArray<long>(10);
// Do nothing
xx = xx;
XPoint point0;
point0.x = 12;
point0.y = 88;
LipingArray<XPoint> xs(10);
// set all points by point0 value
xs.InitData(point0);
// Save the content in a file
xs.SaveFile("arrayDump.bin");
xs.InitData(point0);
// Load a file in buffer
xs.LoadFile("arrayDump.bin");
xs.ReleaseData();
xs = LipingArray<XPoint>(5);
// Load a file with a new buffer. The Size() is 10 after load
xs.LoadFile("arrayDump.bin");
}
3.4 Multi Dimensional Array Usage
LipingArray
supports multi dimensional arrays. The sample code and comments are listed below:
// Two dimensional array:
{
unsigned int dx = 10, dy = 8;
LipingArray<Point, 2> xa(dx, dy);
for (unsigned int x = 0; x < dx; x++)
{
for (unsigned int y = 0; y < dy; y++)
{
Point *p = &xa.Element(x, y);
p->x = x;
p->y = y;
}
}
for (unsigned int x = 0; x < dx; x++)
{
if (printit) printf("\n");
for (unsigned int y = 0; y < dy; y++)
{
Point *p = &xa.Element(x, y);
if (printit) printf("%d,%d ", p->x, p->y);
}
}
if (printit) printf("\n");
for (unsigned int y = 0; y < dy; y++)
{
if (printit) printf("\n");
for (unsigned int x = 0; x < dx; x++)
{
Point *p = &xa.Element(x, y);
if (printit) printf("%d,%d ", p->x, p->y);
}
}
if (printit) printf("\n");
xa.SaveFile("arrayDump_xa.bin"); // Save the content in a file
LipingArray<Point, 2> xl(10, 8);
xl.LoadFile("arrayDump_xa.bin"); // Load the content from file
if (printit) printf("\n");
if (printit) printf("Print loaded two dimensional array:");
for (unsigned int y = 0; y < dy; y++)
{
if (printit) printf("\n");
for (unsigned int x = 0; x < dx; x++)
{
Point *p = &xl.Element(x, y);
if (printit) printf("%d,%d ", p->x, p->y);
}
}
if (printit) printf("\n");
}
// Three dimensional array:
{
unsigned int dx = 5, dy = 3, dz = 8;
LipingArray<CpPoint, 3> xa(dx, dy, dz);
if (printit) printf("Dimensions <%d> Size: ", xa.Dimensions());
for (unsigned int i = 0; i < xa.Dimensions(); i++)
{
if (printit) printf("%d, ", xa.DimSize(i));
}
for (unsigned int x = 0; x < dx; x++)
{
for (unsigned int y = 0; y < dy; y++)
{
for (unsigned int z = 0; z < dz; z++)
{
CpPoint *p = &xa.Element(x, y, z);
p->x = x;
p->y = y;
p->z = z;
}
}
}
for (unsigned int x = 0; x < dx; x++)
{
if (printit) printf("\n");
for (unsigned int y = 0; y < dy; y++)
{
if (printit) printf("\n");
for (unsigned int z = 0; z < dz; z++)
{
CpPoint *p = &xa.Element(x, y, z);
if (printit) printf("%d,%d,%d ", p->x, p->y, p->z);
}
}
}
for (unsigned int z = 0; z < dz; z++)
{
if (printit) printf("\n");
for (unsigned int y = 0; y < dy; y++)
{
if (printit) printf("\n");
for (unsigned int x = 0; x < dx; x++)
{
CpPoint *p = &xa.Element(x, y, z);
if (printit) printf("%d,%d,%d ", p->x, p->y, p->z);
}
}
}
if (printit) printf("\n\n");
// The array content will be released when leaving the section.
}
// Array assignment:
{
unsigned int dx = 3, dy = 5, dz = 10;
LipingArray<Point, 2> xa(dx, dy);
LipingArray<Point, 3> xb(dx, dy, dz);
LipingArray<Point, 2> xa1(dx, dy+1);
xa1 = xa;
//xa1 = xb; // Compile time error. - it is by design
// to avoid confusion at compile time.
if (printit) printf("Dimensions <%d> Size: ", xa.Dimensions());
for (unsigned int i = 0; i < xa.Dimensions(); i++)
{
if (printit) printf("%d, ", xa.DimSize(i));
}
}
3.5 Supported Operators
LipingPtr
supports the following operators:
LipingPtr& operator=(const LipingPtr& inputPtr);
LipingPtr& operator=(T* rawPointer);
T* operator->() const;
T& operator*() const;
bool operator!=(const void* p) const;
bool operator!() const;
bool operator==(const void* p) const;
bool operator!=(const LipingPtr &inputPtr) const;
bool operator==(const LipingPtr &inputPtr) const;
bool operator<(const LipingPtr &inputPtr) const;
Here is the sample code which uses the supported operators:
{
// Different constructors:
LipingPtr<LipingPtrTest> p1(new LipingPtrTest);
LipingPtr<LipingPtrTest> p2(p1);
LipingPtr<LipingPtrTest> p3 = p1;
LipingPtr<LipingPtrTest> p4(NULL);
CallHello(p4);
if (p4 != NULL)
{
p4->Hello(printit);
(*p4).Hello(printit);
}
if (p4 == NULL)
{
if (printit) printf(" Use == operator to check ===> p4 is NULL\n");
}
if (!p4)
{
if (printit) printf(" Use ! operator to check ===> p4 is NULL\n");
}
p4 = p1;
CallHello(p4);
if (p4 != NULL)
{
p4->Hello(printit);
(*p4).Hello(printit);
}
if (p4 == NULL)
{
if (printit) printf(" ===> p4 is NULL\n");
}
LipingPtr<LipingPtrTest> p5(new LipingPtrTest);
LipingPtr<LipingPtrTest> p6(new LipingPtrTest);
if (printit) printf("Call member by -> operator:\n");
p5->Hello(printit);
p6->Hello(printit);
if (printit) printf("Call member by * operator:\n");
(*p5).Hello(printit);
(*p6).Hello(printit);
p1 = p5;
LipingPtr<LipingPtrTest> p7(p1);
LipingPtr<LipingPtrTest> p8 = p1;
LipingPtr<LipingPtrTest> p9(p1);
if (p8 == p1)
{
if (printit) printf("p8 == p1\n");
}
if (p8 != p1)
{
if (printit) printf("p8 != p1\n");
}
p8 = p2;
if (p8 == p1)
{
if (printit) printf("p8 == p1\n");
}
if (p8 != p1)
{
if (printit) printf("p8 != p1\n");
}
}
// more about operator “=” :
{
LipingPtr<ChPoint> xa1 = new ChPoint(1,2);
ChPoint *p2 = new ChPoint(2,2);
xa1 = p2;
LipingPtr<ChPoint> xa2 = new ChPoint(3,1);
xa1 = xa2;
LipingPtr<ChPoint> xa3;
xa3 = xa1 = xa2 = new ChPoint(3,3);
}
4. Use LipingPtr Manage Other Pointers Sample
Here is a sample that shows how to use LipingPtr
to manage Intel OpenCV image pointer.
4.1 Define a De-Allocator for OpenCV IplImage
struct CvReleaseIplImage
{
static void Free(IplImage *p)
{
if (p) cvReleaseImage(&p);
}
};
typedef LipingPtr<IplImage, CvReleaseIplImage> CvImagePtr;
4.2 Use Defined Type to Return CvImagePtr Pointer
The returned image will be released automatically when no one references it.
CvImagePtr GetImageEdge(IplImage * sourceImage, float edgeThresh)
{
IplImage *distImage = cvCreateImage(
cvSize(sourceImage->width,sourceImage->height), IPL_DEPTH_8U, 1);
CvImagePtr distPtr(distImage);
cvCanny(sourceImage, distImage, (float)edgeThresh, (float)edgeThresh*3, 3);
cvNot( distImage, distImage );
return distPtr ;
}
5. Demo Class Definition
5.1 Data Structures Used in Sample Code
const int MaxNameLen = 50;
struct MyRec
{
char name[MaxNameLen];
int age;
};
struct Point { int x; int y; };
struct XPoint { int x; int y; };
struct CpPoint
{
char x;
char y;
char z;
};
struct ChPoint
{
char x;
char y;
ChPoint(char ix, char iy):x(ix), y(iy) {};
};
5.2 LipingPtrTest.h
LipingPtrTest.h file content:
#pragma once
#include "LipingPtr.h"
class LipingPtrTest
{
public:
bool printit;
int index;
static int counter;
public:
LipingPtrTest();
LipingPtrTest(bool printTrace);
~LipingPtrTest();
void Hello(bool printit);
bool operator<(const LipingPtrTest &xobj) const;
};
5.3 LipingPtrTest.cpp
#include "LipingPtrTest.h"
#include <string>
#include <vector>
#include <map>
int LipingPtrTest::counter = 0;
LipingPtrTest::LipingPtrTest() : index(0), printit(true)
{
LIPING_PTR_DEBUG_TRACE(this);
index = counter++;
#ifdef LIPING_PTR_OUTPUT_DEBUG_TRACE
if (printit) printf("Object:%p; index:%d\n", this, index);
#endif
}
LipingPtrTest::LipingPtrTest(bool printTrace) : index(0), printit(printTrace)
{
LIPING_PTR_DEBUG_TRACE(this);
index = counter++;
#ifdef LIPING_PTR_OUTPUT_DEBUG_TRACE
if (printit) printf("Object:%p; index:%d\n", this, index);
#endif
}
LipingPtrTest::~LipingPtrTest()
{
LIPING_PTR_DEBUG_TRACE(this);
}
void LipingPtrTest::Hello(bool printit)
{
LIPING_PTR_DEBUG_TRACE(this);
if (printit) printf("LipingPtrTest [%d] [%p]\n", index, this);
}
bool LipingPtrTest::operator<(const LipingPtrTest &xobj) const
{
bool retval = this->index < xobj.index;
return retval;
}
History
- 29th June, 2008: Initial post