I'm implementing a math library in C++. The library will be compiled to a DLL so those who use it will only need the header files the classes' definitions.
The users of my classes will be people who are new to the language. However, there are some objects that might be referenced in several parts of their programs. Since I don't expect them to do the memory management, I'd like to do it myself. Therefore, I have to implement reference counting (garbage collection is not a possibility).
I want to make that reference counting as transparent as possible, for example...
// 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;
Things get a little bit more tricky when I define surface objects, because some surfaces are defined in terms of two curves:
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
I thought that overriding operator =
for pointers could have helped:
// 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;
}
... but then I found that operator =
is not valid unless it is as a non-static member of a class.
Could anybody possible help me?
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();
}
// ...