我正在用C ++实现一个数学库。该库将被编译为DLL,因此使用它的人只需要类定义的头文件。
我班级的用户将是不熟悉该语言的人。但是,有些对象可能会在程序的几个部分中被引用。由于我不指望他们做内存管理,我想自己做。因此,我必须实现引用计数(垃圾收集不可能)。
我想让那个引用计数尽可能透明,例如......
// Define a Bézier curve
CVecList pts;
pts.Add(Vector(0,0,0));
pts.Add(Vector(0,0,100));
pts.Add(Vector(0,100,0));
pts.Add(Vector(0,100,100));
CCurve* c1 = new CBezier(pts);
// Define a 3rd order B-Spline curve
pts.Clear();
pts.Add(Vector(0,0,0));
pts.Add(Vector(0,200,100));
pts.Add(Vector(0,200,200));
pts.Add(Vector(0,-200,100));
pts.Add(Vector(0,-200,200));
pts.Add(Vector(0,0,0));
CCurve* c2 = new CBSpline(pts,3);
// The Bézier curve object must be deleted automatically
// because the only reference to it has been released
// Similar to IUnknown::Release() in COM
c1 = c2;
当我定义表面对象时,事情变得有点棘手,因为有些表面是用两条曲线定义的:
CVecList pts;
// ...
CCurve* f = new CBezier(pts);
pts.Clear();
// ...
CCurve* g = new CBezier(pts);
// Mixed surface: S(u,v) = (1-v)*f(u) + v*g(u)
CSurface* s = new CMixed(f,g);
// There are two references to the first Bézier curve,
// the first one is f
// the second one is hidden in a member of CMixed
// Something similar applies to the second Bézier curve
我认为覆盖operator =
指针可能会有所帮助:
// This is what I tried, but it's illegal:
typedef CReferenceCounted* PRC;
PRC& operator =(PRC& dest, PRC& source)
{
if (source)
source->AddRef();
if (dest)
dest->Release();
memcpy(&dest,&source,sizeof(PRC));
return dest;
}
...但后来我发现operator =
无效,除非它是一个类的非静态成员。
有人可以帮助我吗?
答案 0 :(得分:11)
您尝试的是为标量类型重载运算符。除了枚举之外,C ++不允许你这样做(除了operator =必须是成员之外)。至少一种类型必须是用户定义的类型。因此,您要做的是将原始指针包装到用户定义的类中,该类重载构造函数,复制构造函数,复制赋值运算符和析构函数以及正确的引用计数。这是boost::shared_ptr
的理想情况,它确实如此:
boost::shared_ptr<CCurve> c1(new CBezier(pts));
与表面相同的处理:
CVecList pts;
// ...
boost::shared_ptr<CCurve> f(new CBezier(pts));
pts.Clear();
// ...
boost::shared_ptr<CCurve> g(new CBezier(pts));
// Mixed surface: S(u,v) = (1-v)f(u) + vg(u)
boost::shared_ptr<CSurface> s(new CMixed(f,g));
继续使用该智能指针,它将自动管理指向对象的生命周期:如果指针的最后一个副本超出范围,则指向的对象将被释放。 shared_ptr
旨在易于使用。尽量避免使用原始指针。看看那些智能指针,它们将使您的程序员能够轻松使用C ++:)
编辑:如果要打包shared_ptr,可以使用pimpl(句柄/正文)习语:
/* ---- wrapper in header file bezier.hpp */
struct CBezier {
CBezier(CVecList const& list);
void do_calc();
// ...
private:
struct CBezierImpl;
boost::shared_ptr<CBezierImpl> p;
};
/* ---- implementation file bezier.cpp */
// private implementation
struct CBezier::CBezierImpl {
CBezierImpl(CVecList const& list);
void do_calc();
// ...
};
CBezier::CBezier(CVecList const& list)
:p(new CBezierImpl(list)) {
}
void CBezier::do_calc() {
// delegate to pimpl
p->do_calc();
}
// ...
答案 1 :(得分:1)
如果您正在设计数学库,请花很多时间考虑您的类是否看起来像int或std :: complex。也就是说,值的行为类似于值。 E.g。
std::vector<math::point3d> pts;
pts.push_back(math::point3d(0,0,0));
pts.push_back(math::point3d(110,0,0));
pts.push_back(math::point3d(0,100,0));
pts.push_back(math::point3d(0,0,100));
CCurve c1 = make_bezier(pts);
答案 2 :(得分:0)
我建议您使用intrusive_ptr而不是shared_ptr来控制可以提高性能和可用性的对象,因为您可以稍后将一个原始指针分配给intrusive_ptr,因为引用计数嵌入在对象中。
答案 3 :(得分:0)
我班级的用户将是不熟悉该语言的人。
您的课程是否专为编程课程设计?
如果是这种情况,我会避免使用指针并仅使用复制构造函数/赋值:
答案 4 :(得分:0)
我同意Guishu和MSalters。即使它不是编程课程,也可以很好地模仿数学外观(例如vector3 = vector1 + vector2等)。
你还可以做的是使用copy-on-write(重新计算是一个逻辑结果),但仅限于内部。这可能会为您提供足够快的分配,消除客户端的堆管理以及与数学符号的相似性。
但请注意,C ++中有数学库(TNT,我的头脑)。你有没有考虑过你的工作?