LipingPtr C++ 模板类
C++ 智能指针模板类实现。
引言
LipingPtr
是一个引用计数的 C++ 智能指针模板类。它跟踪指针的使用情况,并在引用计数达到 0
时删除它。LipingPtr
试图将智能指针最常用的功能结合起来,并将它们表示在单个文件中。保持简单的用法也是 LipingPtr
的目标之一。它可能不包含智能指针可能拥有的所有功能;但它努力避免使用令人困惑的术语。
1.1 设计原则
高性能和易用性是 LipingPtr
的主要设计考虑因素。
1.2 线程安全
LipingPtr
在 Win32 和 Win64 中使用“临界区”来保护共享数据。在 Linux 中,它使用 pthread_mutex_t
进行保护。
1.3 支持自定义析构器
默认情况下,LipingPtr
使用 delete
来释放分配的内存。用户也可以定义自己的析构器来释放资源。
1.4 LipingArray
LipingArray
是一个派生自 LipingPtr
的类。它用于管理多维数组。它还可以将数组内容保存到文件或从文件中加载。LipingArray
在结束时发出 delete []
来释放分配的内存。
1.5 BinData
BinData
是一个预定义的数据类型,它使用 LipingArray
来管理二进制数据。
1.6 FilePtr
它是一个预定义的数据类型,它使用 LipingPtr
来跟踪 FILE 指针。它也用于 LipingArray SaveFile()
和 LoadFile()
。当 FilePtr
超出作用域时,FilePtr
会关闭打开的文件。
2. 构建 LipingPtrTest 演示项目
2.1 Visual Studio 2005 构建
LipingPtrTest.sln 是 Visual Studio 2005 的解决方案文件。您可以选择 Win32 或 Win64 平台的构建。
2.2 Linux 构建
您可以使用 makefile 在 Linux 上构建 LipingPtrTest
。
3. 基本用法
3.1 管理对象指针
创建一个新的类实例,并使用 LipingPtr
来管理它。
示例代码
{
LipingPtr<LipingPtrTest> p1(new LipingPtrTest);
p1->Hello(true);
}
LipingPtrTest
实例 p1
将在代码块结束后被释放。
注意:LipingPtrTest
定义附在本文件末尾。
3.2 管理对象数组指针
3.2.1 使用 LipingArray 管理 LipingPtrTest 数组
这是创建 LipingPtrTest
数组并为每个数组元素调用 Hello
方法的示例代码。
{
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 使用 LipingArray 管理 char 数组
{
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 使用 BinData 管理 unsigned char 数组
此示例显示了如何使用 BinData
管理 unsigned char
数组以及如何将数据保存/加载到/从文件中。
{
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 显式使用析构器
此示例代码显示了如何定义和使用析构器 (CppFree
) 来释放由 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 将 LipingPtr 与 STL Vector 一起使用
此示例创建 LipingPtr
对象并将其放入 STL vector。LipingPtr
管理的内存将在 vector (tvector
) 超出作用域时被释放。
{
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 将 LipingPtr 与 STL Map 一起使用
a. 通过字符串 ID 添加 map 元素
{
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. 添加 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. 将 LipingPtr 对象作为第一个 Map 元素
托管对象 (LipingPtrTest
) 必须支持“<
”运算符,才能将 LipingPtr
实例放入 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. 将 LipingPtr 与 STL Vector 和 Sort 算法一起使用
此示例代码将 LipingPtr
对象放入 STL vector 并使用 STL sort 方法对它们进行排序。
{
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 和 ReleaseData 函数
LipingPtr
和 LipingArray
提供了 Attach
、Detach
和 ReleaseData
函数来直接访问隐藏的原始指针。这些操作将影响所有引用同一原始指针的 LipingPtr
或 LipingArray
对象。如果可以使用其他函数完成任务,则不建议频繁使用这些函数。
以下是一个示例代码,展示了 Attach
和 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.
}
}
此代码部分显示了复杂的 Attach
、Detach
、InitData
、SaveFile
和 LoadFile
函数用法
{
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 多维数组用法
LipingArray
支持多维数组。示例如下,并附有注释
// 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 支持的运算符
LipingPtr
支持以下运算符
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;
以下是使用支持的运算符的示例代码
{
// 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. 使用 LipingPtr 管理其他指针示例
这是一个示例,展示了如何使用 LipingPtr
来管理 Intel OpenCV 图像指针。
4.1 为 OpenCV IplImage 定义析构器
struct CvReleaseIplImage
{
static void Free(IplImage *p)
{
if (p) cvReleaseImage(&p);
}
};
typedef LipingPtr<IplImage, CvReleaseIplImage> CvImagePtr;
4.2 使用定义的类型返回 CvImagePtr 指针
返回的图像将在无人引用时自动释放。
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. 演示类定义
5.1 示例代码中使用的数据结构
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 文件内容
#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;
}
历史
- 2008 年 6 月 29 日:初次发布