简体   繁体   中英

c++ Handle class circular reference problem

I am not sure if the title of my question is correct and clear enough. I am not a very experienced software engineer.

I have been preparing a 3D geometry library which utilizes handles as i dont want to use either delete operators or smart pointers. I have a number of classes which are involved in a class hierarchy (implementation inheritance): ReferenceBase, PointBase, Point2D, Point3D, VectorBase etc. For example, CoordSystem inherits from GeometryBase, PointBase inherits from ReferenceBase and LinePieceBase inherits from GeometryBase. I have corresponding handle classes for each: Handle_GeometryBase, Handle_Point2D, Handle_Plane etc. I implemented the library using handle objects (eg Handle_Point2D) instead of the original classes (eg Point2D). For example the project point method of Plane class is defined like:

Handle_Point3D Plane::project(const Handle_PointBase& point) {}

The handle classes have the same hierarchy reflected (eg Handle_VectorBase inherits from Handle_ReferenceBase). I could not utilize a generic class for the handles because of this inheritance. So, i have defined a handle class for each original class using macros. The handle classes are almost the same accept for a few details so i have defined three macros.

This is the architecture i have:

// Forward declarations for the handle classes
class Handle_Foo;
class Handle_Bar;

class Foo {
    int a;
public:
    Foo(int aa) : a{ aa } {}
    /* copy/move ctors, assignments and dtor */
};

class Bar {
    Handle_Foo f;
public:
    Bar(const Handle_Foo& ff) : f{ ff } {}
    /* copy/move ctors, assignments and dtor */
};

class Handle_Foo {
    Foo* p;
public:
    Handle_Foo() : p{} {}
    Handle_Foo(Foo* pp) : p{ pp } {}
    Handle_Foo(const Handle_Foo& rhs) : p{ new Foo{ *rhs.p } } {};
    Handle_Foo(Handle_Foo&& rhs) noexcept : p{ rhs.p } { delete rhs.p; }
    Handle_Foo& operator=(const Handle_Foo& rhs) {
        p = new Foo{ *rhs.p };
        return *this;
    };
    Handle_Foo& operator=(Handle_Foo&& rhs) noexcept {
        p = rhs.p;
        delete rhs.p;
        return *this;
    }
    Foo* operator->() const { return p; }
    ~Handle_Foo() { delete p; }
    Foo* Get() const noexcept { return p; }
    void Set(Foo* pp) noexcept { p = pp; }
    bool IsNull() const noexcept { return bool(p == nullptr); }
};

class Handle_Bar {
    Bar* p;
public:
    Handle_Bar() : p{} {}
    Handle_Bar(Bar* pp) : p{ pp } {}
    Handle_Bar(const Handle_Bar& rhs) : p{ new Bar{ *rhs.p } } {};
    Handle_Bar(Handle_Bar&& rhs) noexcept : p{ rhs.p } { delete rhs.p; }
    Handle_Bar& operator=(const Handle_Bar& rhs) {
        p = new Bar{ *rhs.p };
        return *this;
    };
    Handle_Bar& operator=(Handle_Bar&& rhs) noexcept {
        p = rhs.p;
        delete rhs.p;
        return *this;
    }
    Bar* operator->() const { return p; }
    ~Handle_Bar() { delete p; }
    Bar* Get() const noexcept { return p; }
    void Set(Bar* pp) noexcept { p = pp; }
    bool IsNull() const noexcept { return bool(p == nullptr); }
};

So the original classes requires the handle classes to be defined as you see.

The handle examples i saw on the inte.net or in the Stroustrup's book (c++ programming language) is defined with a template which calls the default copy and move ctors and the default dtor.

// Handle copy ctor for a template design
template<class T>
class Handle {
    T* p;
public:
    Handle(const Handle& rhs) : p{ new T{ *rhs.p } } {};
    /* Other code */
};

However, my architecture requires the ctors, dtor and assignment operators to be defined.

// Handle copy ctor for my current design
class Handle_Bar {
    Bar* p;
public:
    Handle_Bar(const Handle_Bar& rhs) : p{ new Bar{ *rhs.p } } {};
    /* Other code */
};

The copy ctor requires the copy ctor of Bar class to be defined due to p{ new Bar{ *rhs.p } } .

In summary, the original classes require the definition for the handle classes and the handle classes require the definition of copy/move ctors, asssinments and dtor for the original classes.

For this design pattern i recive the following error for the (Handle_Foo f) member defined in Bar class:

f uses undefined class Handle_Foo

Previously i had a different pattern. I have defined the handle classes in another header file like

Handles.h:

// Forward declarations for the original classes
class Foo;
class bar;
/* The same definitions for the handle classes as above */

Geometry.h

#include "Handles.h"
/* Definitions for the original clasases */

In this case, the original class definitions worked but for the copy/move ctors/assignments and dtor of the handle classes i recieved errors.

In the past i used a framework (Caesam) which used handle classes defined for each object which worked well. But the implementaation of the framework is not open source. Hence, i could not get the architecture they used.

Hence, how can i solve the error i mentioned above:

Error: f uses undefined class Handle_Foo

I have solved the problem. The framework I used Caesam actually uses OpenCascade (OCCT) library for the handles. OCCT can be downloaded from https://www.opencascade.com/ . VS configuration procedure for OCCT is described in this video . Defining and implementation of a handle is as follows:

// The header file: Foo.hxx
#ifndef _Foo_HeaderFile
#define _Foo_HeaderFile

#ifndef _Standard_HeaderFile
#include <Standard.hxx>
#endif
#ifndef _Standard_Handle_HeaderFile
#include <Standard_Handle.hxx>
#endif
#ifndef _Standard_Type_HeaderFile
#include <Standard_Type.hxx>
#endif
#ifndef _Standard_Size_HeaderFile
#include <Standard_Size.hxx>
#endif
#ifndef _Standard_Transient_HeaderFile
#include <Standard_Transient.hxx>
#endif
#ifndef _FooBase_HeaderFile
#include "FooBase.hxx"
#endif

// Forward declaration of the class and the base class
class Foo;
class FooBase;

// Define the handle
DEFINE_STANDARD_HANDLE(Foo, FooBase)

class Foo: public FooBase
{
    // Define the members, ctors, dtor and operators

    // Define the RTTI
    public:
    DEFINE_STANDARD_RTTIEXT(Foo, FooBase)
}

#endif

All classes can be defined similarly. The implementation of RTTI should be performed after all definitions are done. So, its better to do RTTI implementation in cxx files.

// Foo.cxx
#include "Foo.hxx"

// Define RTTI
IMPLEMENT_STANDARD_RTTIEXT(Foo, FooBase)

// Implement the functions

Standard_Transient is the base class for the OCCT. Its better to create an abstract base class for your project inheritting from Standard_Transient

class AbstractBase : public Standard_Transient
{
}

class foo: public AbstractBase
{
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM