C++ 简单引用计数:智能指针
我想使用智能指针实现一个简单的引用计数。变量C++ 简单引用计数:智能指针,c++,templates,smart-pointers,reference-counting,C++,Templates,Smart Pointers,Reference Counting,我想使用智能指针实现一个简单的引用计数。变量pointer表示指向存储对象的指针,reference\u count表示对象副本的总计数 如果我们使用NULL初始化对象:reference\u count=-1,否则reference\u count=1 复制ctor和运算符=增量引用\u计数 析构函数减量引用_计数,若并没有指向该对象的其他引用,则执行其删除 这是我的密码: #ifndef smart_pointer_H #define smart_pointer_H template
pointer
表示指向存储对象的指针,reference\u count
表示对象副本的总计数
- 如果我们使用NULL初始化对象:reference\u count=-1,否则reference\u count=1
- 复制ctor和运算符=增量引用\u计数
- 析构函数减量引用_计数,若并没有指向该对象的其他引用,则执行其删除
#ifndef smart_pointer_H
#define smart_pointer_H
template < typename T > class smart_pointer
{
private:
T* pointer;
int reference_count;
public:
smart_pointer() : pointer(0), reference_count(-1) {}
smart_pointer(T* p) : pointer(p)
{
if (p != NULL)
{
this->reference_count = 1;
}
else
{
this->reference_count = -1;
}
}
smart_pointer(const smart_pointer <T> & p) : pointer(p.pointer), reference_count(p.reference_count + 1) {}
bool operator == (const smart_pointer <T>& p) { return pointer == p.pointer; }
bool operator != (const smart_pointer <T>& p) { return pointer != p.pointer; }
~ smart_pointer()
{
if(-- reference_count == 0)
{
std::cout << "Destructing: " << '\n';
delete pointer;
}
}
T& operator * () { return *pointer; }
T* operator -> () { return pointer; }
smart_pointer <T> & operator = (const smart_pointer <T> & p)
{
if (this != &p)
{
if( -- reference_count == 0)
{
delete pointer;
}
pointer = p.pointer;
reference_count = p.reference_count + 1;
}
return *this;
}
};
})
感谢您的帮助…您的参考计数不起作用 如果将两个智能指针复制或分配到一起,则它们需要使用相同的位置来执行计数 目前,每个对象都有自己的计数,因此它们可能会变得不同步
smart_pointer<int> x(new x); // x.pointer: <good> x.reference_count: 1
{
smart_pointer<int> y; // y.pointer: NULL y.reference_count: -1
y = x; // x.pointer: <good> x.reference_count: 1
// y.pointer: <good> y.reference_count: 2
smart_pointer<int> z;
x = z; // x.pointer: NULL x.reference_count: 0 (BAD)
// z.pointer: NULL z.reference_count: -1
// y.pointer: <bad> (it was deleted by x) y.reference_count: 2
}
如果现在有人试图使用“y”,我们就有了未定义的行为,因为它包含一个指向已取消分配的内存的指针
编辑经典(但过于简单的智能指针:
#包括
模板
类SP
{
T*对象;
大小*计数;
公众:
SP(T*数据)
尝试
//使用奇怪的试用初始值设定项列表捕捉新的投掷。
//如果确实如此,我们将删除数据以防止其泄漏。
:对象(数据)
,计数(数据?新整数(1):空)
{/*这是构造函数*/}
捕获(…)
{删除数据;}
SP():对象(NULL),计数(NULL){}
//SP(T*data):对象(data),count(newint(1)){}//排列在这里,看起来很整洁,但是上面实现了使用奇怪的try-catch
SP(SP const&rhs):对象(rhs.object),计数(rhs.count){if(count){++(*count);}
SP&operator=(SP-rhs)//注意rhs中的隐式复制构造
{
//使用复制交换习惯用法进行赋值。
//副本被隐藏,因为参数是按值传递的。
此->交换(rhs);
归还*这个;
}
无效交换(SP和rhs)抛出()
{
标准::交换(对象,rhs.object);
标准::交换(计数,右侧计数);
}
~SP()
{
如果((计数)和(-(*count)==0))
{
删除计数;
删除对象;
}
}
};
我相信你做得很好,但是为什么不使用其中一个或中可用的智能指针呢?这两个都是经过多年改进的产品。我们可以假设这只是一个练习来帮助你理解吗?PS.你忘了复制构造函数。是的,这只是循环引用测试的一个例子,但在将来我想实现它我的应用程序中有这样的智能指针。实现您自己的是一个坏主意。获得正确的智能指针比看起来要困难得多。请使用经过同行评审的库(C++0x std::shared_ptr/C++03 boost::shared_ptr或std::tr1::shared_ptr)提供的智能指针他们花了很长时间才解决了这些智能指针中的错误,但现在它们工作得很好。我甚至从未注意到共享指针是新标准C++0x的一部分……C++0x std::shared_ptr和std::tr1::shared_ptr之间有什么区别吗?即使忽略他的代码和Martin的评论,我也会高度怀疑他做得很好。做一个很好的工作,实现智能指针是非常困难的。我遇到了很少人,甚至可以想到所有潜在的问题(我不是少数天才)。即使是Scott Myers(C++的名声)试图在一个出版物中实现智能指针,他仍然在10年后得到bug报告。(或者他在为当地C++用户做的一次演讲中说)所以如果Scott Myers做得不好,我肯定不会去尝试。@布瑞恩:我试图对他“轻松”。))马丁:当我说“不理会马丁的评论”时。,我的意思是完全无视你说他的代码是错误的。我认为Ian的代码被破坏是理所当然的,仅仅是因为他试图实现智能点,而不是基于对他的代码的任何了解。也就是说,“这太难了,我只能假设你犯了一个错误。我不需要实际检查你的代码,看看它是否工作;它肯定在某个地方有致命的错误。”。"谢谢你的评论。但是我找不到问题,也许我没有正确理解问题。smart_指针z;x=NULL;//x.ref_count=0,析构函数中不会执行任何删除操作=>没有内存泄漏。使用新的复制构造函数,代码中也会出现同样的漏洞。+1:引用计数的最基本原则g表示每个对象都有自己的引用计数。您将它们保存在每个指针中-因此,当任何给定指针更新引用计数时,其他指针都不会得到通知。@DeadMG:好的。每个智能指针都必须有引用对象的列表,并且在更改其状态后更新列表中的所有对象…@Ian:我认为DeadMG所说的是:每个dynamic对象(非智能指针)有自己的引用计数。因此,当您分配智能指针时,您需要在内部进行两次赋值。1)动态对象。2) 动态对象计数。现在两个智能指针使用相同的计数器。所以你只需要增加它。
#include "Point.h"
class PointsList
{
private:
std::vector <smart_pointer <Point> > points;
public:
smart_pointer <Point> & operator [] ( int index ) {return points[index];}
public:
void push_back(smart_pointer <Point> p) {points.push_back(p);}
void erase(unsigned int index) {points.erase(points.begin() += index );}
void printPoints()
{
std::cout << "List of points" << '\n';
for (unsigned int i = 0; i < points.size(); i++)
{
points[i]->print();
}
}
};
#include "Point.h"
#include "PointsList.h"
int main()
{
smart_pointer <Point> pb = NULL;
pb = (new Point(0,0));
smart_pointer <Point> p0(new Point(0,0));
p0->print();
smart_pointer <Point> p1(new Point(10,10));
p1->print();
smart_pointer <Point> p2(new Point(20,20));
p2->print();
smart_pointer <Point> p3(new Point(30,30));
p3->print();
smart_pointer <Point> pa(p3);
p0->setP1(p2);
p0->setP2(p3);
p0->print();
p0 = p1;
p0->print();
p0->print();
PointsList pl1;
pl1.push_back(p0);
pl1.push_back(p1);
PointsList pl2;
pl2.push_back(p2);
pl2.push_back(p3);
pl1.erase(0);
pl1.printPoints();
pl2.printPoints();
return 0;
}
//Class with cross-references to points p1, p2
class PointTopo
{
private:
double x, y;
PointTopo * p1;
Point * p2;
public:
PointTopo(double xx, double yy): x(xx), y(yy) {this-> p1 = NULL; this->p2 = NULL;}
...
//Class with cross references: topological model for Delaunay triangulation
class Edge
{
private:
Point2D * start;
Edge *next;
Edge *previous;
Edge *twin;
...
};
smart_pointer<int> x(new x); // x.pointer: <good> x.reference_count: 1
{
smart_pointer<int> y; // y.pointer: NULL y.reference_count: -1
y = x; // x.pointer: <good> x.reference_count: 1
// y.pointer: <good> y.reference_count: 2
smart_pointer<int> z;
x = z; // x.pointer: NULL x.reference_count: 0 (BAD)
// z.pointer: NULL z.reference_count: -1
// y.pointer: <bad> (it was deleted by x) y.reference_count: 2
}
x { pointer: 0xabcd1234 reference_count: 1 }
y { pointer: 0xabcd1234 reference_count: 2 }
z { pointer: NULL reference_count: -1 }
// So here is your assignment operator.
// Doing the `x = z` we will walk through the following code.
//
smart_pointer <T> & operator = (const smart_pointer <T> & p)
{
if (this != &p)
{
// We get here.
// Left hand side is 'x' so this condition will be true.
if( -- reference_count == 0)
{
// Now we are deleting a pointer.
// That is held by 'x'
delete pointer;
// But 'y' is holding a pointer with the same value.
// Now y is holding a pointer to a deleted variable.
}
// Here we copy 'z' into 'x'
// Copy the pointer. That happens to be NULL.
pointer = p.pointer;
// Now we copy and increment the reference count.
// So 'x' has a value of 0 while 'z' has a value of -1.
// This also breaks the invariant on 'x' that NULL values should
// have a reference count of -1 (as X is NULL and ref-count is 0)
reference_count = p.reference_count + 1;
}
return *this;
}
#include <vector>
template<typename T>
class SP
{
T* object;
size_t* count;
public:
SP(T* data)
try
// Use weird try around initializer list to catch new throwing.
// If it does we delete data to stop it from leaking.
:object(data)
,count(data ? new int(1) : NULL)
{ /* This is the constructor */}
catch(...)
{delete data;}
SP(): object(NULL), count(NULL) {}
//SP(T* data): object(data), count(new int(1)) {} // Lined up here so it look neat but implemented above to use weird try catch
SP(SP<T> const& rhs): object(rhs.object), count(rhs.count) {if (count) {++(*count);}}
SP<T>& operator=(SP<T> rhs) // Note implicit copy construction in rhs
{
// Using copy swap idiom for assignment.
// The copy is hidden because the parameter is pass by value.
this->swap(rhs);
return *this;
}
void swap(SP<T>& rhs) throw()
{
std::swap(object, rhs.object);
std::swap(count, rhs.count);
}
~SP()
{
if ((count) && (--(*count) == 0))
{
delete count;
delete object;
}
}
};