LipingPtr C++ Template Class
The last source code update date: 08/30/2008
Web: www.lipingshare.com/LipingPtr
Email: LipingShare@gmail.com
Title: LipingPtr C++ Template Class
Author: Liping Dai
Email: lipingsharemail@gmail.com
Language: C++
Platform: Win32, Win64, Linux 2.6
Technology: C++, STL
Description: A C++ Smart Pointer Template Class Implementation
Section C++
· Download LipingPtr - 4.41 KB
· Download LipingPtrTest - 13.89 KB
Table of Content
1.3 Support Custom De-Allocator
2. Build LipingPtrTest Demo Project
3.2 Manage Object Array Pointer
3.2.1 Manage LipingPtrTest array by using LipingArray
3.2.2 Manage char array by using LipingArray
3.2.3 Manage unsigned char array by BinData
3.2.4 Explicitly Use De-Allocator
3.2.5 Use LipingPtr with STL Vector
3.2.6 Use LipingPtr with STL Map
3.3 Attach, Detach, and ReleaseData Functions
3.4 Multi Dimensional Array Usage
4. Use LipingPtr Manage Other Pointers Sample
4.1 Define a De-Allocator for OpenCV IplImage
4.2 Use Defined Type to Return CvImagePtr Pointer
5. Use Base Class LipingPtr Manage Descendent Class Pointers
6.1 Data Structures Used in Sample Code
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 together and represent them in a single file. Keep the usage simple is the main goal of LipingPtr. It may not include all the functionalities smart pointers cold have; but it tried hard to keep the usages simple.
High performance and easy to use are the major design considerations for LipingPtr.
LipingPtr uses “Critical Section” in Win32 and Win64 for the shared data protection. It uses “pthread_mutex_t” for the protection in Linux.
By default, LipingPtr uses "delete" to release the allocated memory. The user can also define own De-Allocator to release the resources.
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.
BinData is a predefined data type which uses LipingArray to manage binary data.
It is a predefined data type which uses LipingPtr to keep track of FILE pointer. It is also been used in LipingArray SaveFile() and LoadFile(). FilePtr closes the opened file(s) when the FilePtr goes out of the scope.
LipingPtrTest.sln is the solution file for Visual Studio 2005. You can select build for Win32 or Win64 platforms.
You can use the makefile to build LipingPtrTest on Linux.
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 out of the code section.
Note: LipingPtrTest definition is attached at the end of this file.
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);
}
}
{
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());
}
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");
}
This sample code shows how to define and use a de-allocator (CppFree) to releae 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);
}
This sample creates LipingPtr object and put it in STL vector. The LipingPtr managed memory will be released when the vector (tvector) go out of the scope.
This sample also shows the base class LipingPtr Vector can contain different pointers in the same descendent class tree.
if (printit) printf("Use LipingPtr with STL Vector:\n");
if (printit) printf("The vector contains the base and derived classes pointers.\n");
{
std::vector< LipingPtr<LipingPtrTest> > tvecotr;
for(int i=0; i < 6; i++)
{
// Directly put the new pointer into vector:
if (printit) printf(" >> begin add %d\n", i);
if (i % 3 == 0)
{
tvecotr.push_back( LipingPtr<LipingPtrTest>(new LipingPtrTest()) );
}
else if (i % 3 == 1)
{
tvecotr.push_back( LipingPtr<LipingPtrTest>(new LipingPtrTestD()) );
}
else
{
tvecotr.push_back( LipingPtr<LipingPtrTest>(new LipingPtrTestDD()) );
}
// Assign the new pointer to a temp base pointer, and put it in a vector that
// is defined as the base class LipingPtr.
LipingPtrTest* tmpPtr;
if (i % 3 == 0)
{
tmpPtr = new LipingPtrTest();
tvecotr.push_back( tmpPtr );
}
else if (i % 3 == 1)
{
tmpPtr = new LipingPtrTestD();
tvecotr.push_back( tmpPtr );
}
else
{
tmpPtr = new LipingPtrTestDD();
tvecotr.push_back( tmpPtr );
}
if (printit) printf(" << end add %d\n", i);
}
for(unsigned int i=0; i < tvecotr.size(); i++)
{
tvecotr[i]->Hello(printit);
}
}
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");
}
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 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 keept 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");
}
LipingArray supports multi dimensional arrays. The sample code and comments is 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));
}
}
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);
}
Here is a sample that shows how to use LipingPtr to manage Intel OpenCV image pointer.
struct CvReleaseIplImage
{
static void Free(IplImage *p)
{
if (p) cvReleaseImage(&p);
}
};
typedef LipingPtr<IplImage, CvReleaseIplImage> CvImagePtr;
The returned image will be released automatically when no one reference to 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 ;
}
If LipingPtr is declared for a base class, it will be able to manage all the descendent class pointers. From which, C++ polymorphism is kept.
// Use Base Class LipingPtr Manage Descendent Class Pointers
{
LipingPtr<LipingPtrTest> tmpPtr;
tmpPtr = new LipingPtrTest();
tmpPtr->Hello(printit);
tmpPtr = new LipingPtrTestD();
tmpPtr->Hello(printit);
tmpPtr = new LipingPtrTestDD();
tmpPtr->Hello(printit);
}
The sample code for LipingPtr Vector also shows this usage.
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) {};
};
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();
virtual void Hello(bool printit);
bool operator<(const LipingPtrTest &xobj) const;
};
class LipingPtrTestD : public LipingPtrTest
{
public:
void Hello(bool printit);
};
class LipingPtrTestDD : public LipingPtrTestD
{
public:
void Hello(bool printit);
};
#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;
}
void LipingPtrTestD::Hello(bool printit)
{
LIPING_PTR_DEBUG_TRACE(this);
if (printit) printf("LipingPtrTestD [%d] [%p]\n", index, this);
}
void LipingPtrTestDD::Hello(bool printit)
{
LIPING_PTR_DEBUG_TRACE(this);
if (printit) printf("LipingPtrTestDD [%d] [%p]\n", index, this);
}
Copyright (c) 2008 Liping Dai. All rights reserved.
Web: www.lipingshare.com
Email: lipingsharemail@gmail.com
Copyright and Permission Details:
=================================
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, and/or sell copies
of the Software, subject to the following conditions:
1. Redistributions of the source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
THE SOFTWARE PRODUCT IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF TITLE, NON-INFRINGEMENT,
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
Update History:
07/22/2008: Initial Post
08/21/2008: Added ArraySizeType for LipingArray
The default ArraySizeType is “unsigned long”.
08/29/2008: Changed LipingArray default array size type to size_t.
size_t uses 4 bytes for 32 bits builds; 8 bytes for 64 bits builds.
The user can expecially redefine the array size type; for example:
typedef LipingArray<CpPoint, 3, CppDeleteArray<CpPoint>, size_t> TDArray;
TDArray::ArraySizeType dx = 5, dy = 3, dz = 8;
TDArray xa(dx, dy, dz);
if (printit) printf("Sizeof(ArraySizeType) = %d; Dimensions <%d>; Sizes for each dimension: ",
sizeof(TDArray::ArraySizeType), xa.Dimensions());
for (unsigned int i = 0; i < xa.Dimensions(); i++)
{
if (printit) printf("%d, ", xa.DimSize(i));
}
08/30/2008:
1. Added “Use Base Class LipingPtr Manage Descendent Class Pointers” description.
2. Modified Sample Code for Using LipingPtr with STL Vector
The sample shows the base class LipingPtr Vector can contain different pointers
in the same descendent class tree.
_ END _