Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VC9.0

LipingPtr C++ Template Class

4.04/5 (13 votes)
29 Jun 2008CPOL4 min read 1   310  
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

C++
{
    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.

C++
{
    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

C++
{
    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.

C++
{
    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.

C++
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.

C++
{
    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
C++
{
    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
C++
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.

C++
{
    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.

C++
{
    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:

C++
{
    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:

C++
{
    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:

C++
// 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:

C++
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:

C++
{
    // 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

C++
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.

C++
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

C++
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:

C++
#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

C++
#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

License

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