Question

This part of the question provides background information and can be ignored

I am working on a template library which heavily relies on the use of the curiously recurring template pattern. The idea behind the class structure is that the user can either

1). Use predefined classes with standard methods. These classes are very simple leafs of the base class that only provide constructors/destructor, declare the variable members and declare the base class(es) as friend(s). All methods that operate on the variable members of the derived classes are defined in the base class(es).

2). Use the base classes to create his/her own extensions. This method also allows users to introduce their own methods that operate on the same variable members.

Only a single level inheritance is enforced by design.

My question is, primarily, about the clause 2. In the current implementation the user has to define all constructors implicitly (i.e. describe full process of memory allocation for dynamic variable members of the class, etc).

Question

The example below demonstrates an investigation into a possibility to use the CRTP to provide the definition of the allocation of the memory of the heap variables of the derived classes in the base class constructors.

Part of the base class

template<class TLeafType, class MyClass> class sysBaseDiscreteTrajectoryPoint {
 ...

//one of the base constructors
sysBaseDiscreteTrajectoryPoint(const MyClass& MyClassInstance) {
    std::cout << "Base additional constructor called" << std::endl;
    std::cout << asLeaf().Point << std::endl;
    asLeaf().Point=new MyClass(MyClassInstance);
    std::cout << asLeaf().Point << std::endl;
}

TLeafType& asLeaf(void) {
return static_cast<TLeafType&>(*this);
}

...
};

The derived class:

template<class MyClass> 
class sysDiscreteTrajectoryPoint: public sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass> {
...
friend class sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass>;
private:
    MyClass* Point;
public:
    sysDiscreteTrajectoryPoint(const MyClass& MyClassInstance): sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass>(MyClassInstance){
        std::cout << "Derived additional constructor called " << std::endl; 
        std::cout << Point << std::endl;
        std::cout << *Point << std::endl;
    }
...
}

main:

int a(5);
sysDiscreteTrajectoryPoint<int> A(a);

The code produces the following output:

Base additional constructor called
0x847ff4
0x8737008
Derived additional constructor called 
0x8737008
5
Derived destructor called 
Base destructor called 

The output suggests that the concept may be feasible. However, I have two questions.

1). I would like to ensure that I understand all processes that happen during the execution of the code. In particular, I am interested in the efficiency of the process, as I may need to instantiate a substantial amount of objects from the classes presented above and I would like to understand what happens with Point (are there any hidden redefinitions?)

2). The question is related to the use of the library boost for the definition of smart pointers for the members of the derived class. When I tried replacing the raw pointer with boost::shared_ptr, I received a segmentation fault error when trying to allocate memory for the member of the derived class through the base class. The important sections of the code are shown below.

Part of the base class:

template<class TLeafType, class MyClass> class sysBaseDiscreteTrajectoryPoint {
 ...

//one of the base constructors
sysBaseDiscreteTrajectoryPoint(const MyClass& MyClassInstance) {
    std::cout << "Base additional constructor called" << std::endl;
    std::cout << asLeaf().Point << std::endl;
    asLeaf().Point.reset(new MyClass(MyClassInstance));
    std::cout << asLeaf().Point << std::endl;
}

TLeafType& asLeaf(void) {
return static_cast<TLeafType&>(*this);
}

...
};

Part of the derived class:

template<class MyClass> 
class sysDiscreteTrajectoryPoint: public sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass> {
...
friend class sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass>;
private:
    boost::shared_ptr<MyClass> Point;
public:
    sysDiscreteTrajectoryPoint(const MyClass& MyClassInstance): sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass>(MyClassInstance){
        std::cout << "Derived additional constructor called " << std::endl; 
        std::cout << Point << std::endl;
        std::cout << *Point << std::endl;
    }
...
}

main:

int a(5);
sysDiscreteTrajectoryPoint<int> A(a);

The code produces the following output:

Base additional constructor called
0x28d324
Segmentation fault

I have also tried scoped_ptr. However, it failed at run time but with a different error:

Base additional constructor called
*** glibc detected *** ./TestSystem: free(): invalid pointer: 0x00d3fff4 ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x6b961)[0xc4e961]
...

I assume that it is related to the specifics of the operation of boost smart pointers. Does anyone know how to resolve this issue?

Was it helpful?

Solution

The address of the shared_ptr is known at compile time for the reasons given in the above answers, but the shared_ptr itself is still uninitialised because the derived-class constructor has not yet been called, and thus has not had a chance to implicitly call its instance members' constructors, including the default constructor for shared_ptr. Therefore, when you call reset() to assign the shared_ptr, it first tries to release (and possibly delete) the object at whatever spurious address it contains (to avoid leaking an existing referent) before assigning and referencing the new object. That first step, I believe, is what causes the segfault.

If the shared_ptr constructor ran first, it would null its contained raw pointer, preventing the subsequent reset() call from trying to release an object at the spurious address.

Using asLeaf() to access the derived class from the base-class constructor is inherently unsafe for non-POD types because construction is incomplete (the derived class's members are not yet constructed). This is, incidentally, why virtual method calls from a base constructor will never call overrides from more-derived classes - the language explicitly prevents overrides from being called until construction of the whole object is complete because in most cases the state of the whole object is not yet defined.

There may be better solutions for you, but one approach that would work would be to remove that initialization code from the base class's constructor and put it in an init() function that is called explicitly at every instantiation of the derived class. init() can still live in the base class, but it's safer because everything will have been initialized by the time it runs.

Side note: avoid putting small objects in shared_ptr without good reason. You might have a legitimate need for it in this case, but in general I prefer direct aggregation of members to single-owner pointers and single-owner pointers to shared pointers wherever possible because the overhead escalates. Single-owner pointers involve heap allocations, and shared pointers also add to this the cost of counting/tracking owners so that the object can be deleted when unreachable.

OTHER TIPS

How is it possible that you can access Point member belonging to the derived class from base constructor? When the base constructor is being invoked, the derived class part does not exist. Perhaps it works just "by accident".

But it certainly fails with shared_ptr, because you attempt to assign it before it has a chance to get initialized.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top