bgrupczy
bgrupczy

Reputation: 67

Base class needs reference to derived class member not yet constructed

Is there a way to ensure that an object created by derived class that is passed by reference to the base class is already constructed and still have the inheritance?

This is the base of the question:

class ClassA : public ClassA_Base {
public:
  ClassA(int maxSize = 4000)
  : ClassA_Base(xClassB_Object),
    xClassB_Object(maxSize) {};

private:
  ClassB xClassB_Object;
};

This is the closest answer I could find. But they are not trying to use inheritance: how to prevent usage of class members not yet constructed?

EDIT 1: I implemented the solution from Sam Varshavchik.

class ClassA : private ClassB, public ClassA_Base {
public:
  ClassA(int maxSize = 4000)
  : ClassB{maxSize}, 
    ClassA_Base{static_cast<ClassB &>(*this)} {}
};

Obviously the function names I'm using aren't the same as the example code in question and all levels of AppLevel, derived, and base have much more code in them. My code now accesses the base functions directly instead of through xClassB_Object so I lose a "qualifier". "Where did that function come from that has little to do the the AppLevel/ClassA functionality?!"

To try to alleviate the "ambiguity", I'm using more qualification on the calls. Instead of just:

ClassB_Action()

Looking at the fully qualified call:

this->ClassB_Base::ClassB_Action()

I remove "this->" and am using the following:

ClassB_Base::ClassB_Action()

/EDIT 1:

Here is the more detailed version of the question.

I have a chain of classes. Some are workers and some allocate for the workers. This gives the most flexibility for using the classes.

My intent is for a user to derive off of any level in the chain without them having to implement anything to support the base class other than calling constructors. Then use the member functions of the base class directly.

The problem is that due to the order that constructors are called, my objects are not fully constructed when their reference is passed to the constructor of the base class. I know that I can pass the reference at this point but I just can not use it in the base class constructor.

This is the whole chain of classes:

ClassB - moves data into memory (does not need ClassA)
ClassA - modifies the data and passes to ClassB

ClassB_Base - Worker (Operates on buffer)
ClassB - Allocator (Creates a buffer)
ClassA_Base - Worker (Operates on data and calls ClassB worker)
ClassA - Allocator (Creates a ClassB)
AppLevelClass - Top layer passing in data
//// ClassB_Base acts on the data
class ClassB_Base {
public:
  ClassB_Base(int* pData, int* pIndex, int length) 
  : xpData(pData),
    xpIndex(pIndex),
    xLength(length) {}

  void ClassB_Action(int* data) {
    //// Do ClassB_Action stuff with the data
    xpData = xpIndex = &xLength; // Junk code to eliminate compiler warning
  }

private:
  int*    xpData;
  int*    xpIndex;
  int     xLength;
};
//// ClassB only allocates a data buffer for use in ClassB_Base
class ClassB : public ClassB_Base
{
public:
  ClassB(int maxSize)
  : ClassB_Base(new int[maxSize], &xIndex, maxSize) {}

private:
  int   xIndex;
};
//// ClassA_Base acts on the data and calls ClassB
class ClassA_Base {
public:
  ClassA_Base(ClassB_Base& ClassB_Passed_Ref)
  : xClassB_Object_Ref(ClassB_Passed_Ref) {}

  void ClassA_Action(int *data) {
    //// Do some ClassA_Action stuff with the data like sort or filter
    xClassB_Object_Ref.ClassB_Action(data);
  }

private:
  ClassB_Base& xClassB_Object_Ref;
};
//// ClassA only instantiates a class for use in ClassA_Base
class ClassA : public ClassA_Base {
public:
  ClassA(int maxSize = 4000)
  : ClassA_Base(xClassB_Object),
    xClassB_Object(maxSize) {}

private:
  ClassB xClassB_Object;
};

Use cases:

//// Now using the whole chain
class AppLevelClass1 : public ClassA {
public:
  AppLevelClass1(void) {
    ClassA_Action(xData);
  }

  int xData[10];
};
//// Or using part of the chain
class AppLevelClass2 : public ClassB {
public:
  AppLevelClass2(void) {
    ClassB_Action(xData);
  }

  int xData[10];
};
//// Or using another part of the chain
class AppLevelClass3 : public ClassB_Base {
public:
  AppLevelClass3(void) : ClassB_Base(xBuff, &xIndex, 100) {
    ClassB_Action(xData);
  }

  int xData[10];
  int xIndex;
  int xBuff[1000];
};
//// Other use cases
int main() {
  int data[10];
  int index;
  AppLevelClass myAppLevelClass;
  myAppLevelClass.ClassA_Action(data);

  ClassB_Base xClassB_Base(data, &index, 5);
  xClassB_Base.ClassB_Action(data);

  ClassB xClassB(1000);
  xClassB.ClassB_Action(data);

  ClassA_Base xClassA_Base1(xClassB_Base);
  xClassA_Base1.ClassA_Action(data);

  ClassA_Base xClassA_Base(xClassB);
  xClassA_Base.ClassA_Action(data);

  ClassA xClassA(1000);
  xClassA.ClassA_Action(data);
}

Upvotes: 0

Views: 236

Answers (1)

Sam Varshavchik
Sam Varshavchik

Reputation: 118300

In C++, a base class always gets constructed before any part of the derived class gets constructed. No exceptions to this, no workarounds, no alternatives. No part of the derived class exists until the base class gets fully constructed, and its constructor (if any) returns. This is fundamental C++.

However

Another fundamental part of C++ is multiple inheritance, and with multiple inheritance all base classes always get constructed in declaration order (I am ignoring virtual inheritance, which adds some additional complications which are not relevant here).

This is pretty much the only tool you have at your disposal for tweaking the order in which things get constructed. But this can be used carefully to your advantage. For example, instead of the code shown in your first example:

class ClassA : private ClassB, public ClassA_Base {
public:
  ClassA(int maxSize = 4000)
  : ClassB{maxSize}, 
    ClassA_Base{static_cast<ClassB &>(*this)}
  {
  }
};

Instead of having ClassB a private member of ClassA, you privately inherit from it; and then pass a reference to it to ClassA_Base's constructor. ClassB gets fully constructed first, then ClassA_Base gets a reference to it.

Upvotes: 1

Related Questions