Reference Counting in C++

纵然是瞬间 提交于 2019-12-03 15:17:29

What you tried was to overload an operator for scalar types. C++ doesn't allow you to do that except for enumerations (beside the point that operator= has to be a member). At least one of the types has to be a user defined type. Thus, what you want to do is to wrap the raw pointer into a user defined class, which overloads constructor, copy constructor, copy assignment operator and destructor an do the proper reference counting. This is an ideal situation for boost::shared_ptr, which does exactly that :

boost::shared_ptr<CCurve> c1(new CBezier(pts));

The same deal with surfaces:

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

Carry around that smart pointer, and it will automatically manage the life-time of the pointed to object: If the last copy of the pointer goes out of scope, the object pointed to is freed. shared_ptr is designed to be easy to use. Try to avoid working with raw pointers as much as you can. Have a look at those smart pointers, they will ease your programmers live with C++ :)

Edit: If you are going to wrap a shared_ptr, you can do so using the pimpl (handle/body) idiom:

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

// ...

If you're designing a math library, spend a lot of time thinking whether your classes can look like int or std::complex. That is to say, have values behave like values. 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);

I'd recommend intrusive_ptr instead of shared_ptr for objects you can control for better performance and usability, as you can assign a raw pointer to intrusive_ptr later, because the reference count is embedded in the object.

The users of my classes will be people who are new to the language.

Is your class designed for a programing course ?

If this is the case, I would avoid using pointers and use only copy constructors / assignation:

  • Performance / Memory is not a priority
  • Doing the memory management yourself will show a pretty bad example on how to use new/delete
  • Using any kind of smart pointer without knowing about memory management could cause a lot of confusion later on.

I agree with Guishu and MSalters. Even if it's not for a programming course, it may be nice to imitate more closely maths look (e.g. vector3 = vector1+vector2 etc).

What you could also do is to use copy-on-write (refounting being a logical consequence), but only internally. That may give you fast-enough assignments, eliminate heap management on the client-side and similarity to math notation.

Note, however, that there are math libraries available for C++ (TNT, off the top of my head). Did you consider basing your work on that?

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!