【问题标题】:Reference Counting in C++C++ 中的引用计数
【发布时间】:2010-09-27 02:19:38
【问题描述】:

我正在用 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 = 是无效的,除非它是类的非静态成员。

谁能帮帮我?

【问题讨论】:

  • 我一直在努力保持我的代码模板免费,但我想不出其他解决方案。谢谢。
  • Eduardo 你能编辑这篇文章,选择你问题中的代码,然后点击带有 0 和 1 的按钮吗?这将对代码应用语法高亮显示。
  • 我不知道这个技巧。 (我是这里的新手)谢谢,TM。

标签: c++ reference counting


【解决方案1】:

您尝试的是重载标量类型的运算符。除了枚举(除了 operator= 必须是成员之外),C++ 不允许您这样做。至少其中一种类型必须是用户定义的类型。因此,您要做的是将原始指针包装到用户定义的类中,该类重载构造函数、复制构造函数、复制赋值运算符和析构函数,并进行正确的引用计数。这是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 (handle/body) 习惯用法:

/* ---- 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();
}

// ...

【讨论】:

  • 我会 dpo 类似将 CCurve 定义为 CCurved_internal,然后 typedef CCurve 到 boost::shared_ptr ,,,
  • 我一直在努力保持我的代码模板免费,但我想不出另一种解决方案。谢谢。
  • 你可以,如果你包装使用 CCurve 作为非指针,并在其中分配资源。然后 CCurve 可以进行引用计数,在需要时删除托管指针。你可以只包装一个 shared_ptr,让它对你的学生透明。
  • 请避免重新实现引用计数。让它在多线程环境中工作比你想象的要困难得多。此外 Boost 的 shared_ptr 实现也很棒。
  • 是的,保罗,这就是包装 shared_ptr 的想法。我也认为没有理由重新发明轮子
【解决方案2】:

如果您正在设计一个数学库,请花大量时间考虑您的类是否可以看起来像 int 或 std::complex。也就是说,值的行为类似于值。例如

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);

【讨论】:

    【解决方案3】:

    我建议使用 intrusive_ptr 而不是 shared_ptr 用于您可以控制的对象以获得更好的性能和可用性,因为您可以稍后将原始指针分配给 intrusive_ptr,因为引用计数嵌入在对象中。

    【讨论】:

      【解决方案4】:

      我的课程的用户将是该语言的新手。

      你的课程是为编程课程设计的吗?

      如果是这种情况,我会避免使用指针,只使用复制构造函数/赋值:

      • 性能/内存不是优先事项
      • 自己进行内存管理会显示一个关于如何使用 new/delete 的非常糟糕的示例
      • 在不了解内存管理的情况下使用任何类型的智能指针可能会在以后造成很多混乱。

      【讨论】:

        【解决方案5】:

        我同意 Guishu 和 MSalters 的观​​点。即使不是编程课程,也可以更好地模仿数学外观(例如 vector3 = vector1+vector2 等)。

        您还可以做的是使用写时复制(重新编写是一个合乎逻辑的结果),但仅限于内部。这可能会为您提供足够快的分配,消除客户端的堆管理以及与数学符号的相似性。

        但是,请注意,有一些可用于 C++ 的数学库(TNT,我想不到)。您是否考虑过以此为基础开展工作?

        【讨论】:

          猜你喜欢
          • 2014-12-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-28
          相关资源
          最近更新 更多