Базовый класс нуждается в ссылке на член производного класса, который еще не создан - PullRequest
0 голосов
/ 07 ноября 2019

Есть ли способ убедиться, что объект, созданный производным классом, который передается по ссылке в базовый класс, уже создан и все еще имеет наследование?

Это базовыйвопроса:

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

private:
  ClassB xClassB_Object;
};

Это самый близкий ответ, который я смог найти. Но они не пытаются использовать наследование: как предотвратить использование еще не созданных членов класса?

РЕДАКТИРОВАТЬ 1: Я реализовал решение от Сэма Варшавчика.

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

Очевидно, что имена функций, которые я использую, не совпадают с рассматриваемым примером кода, и все уровни AppLevel, производного и базового содержат гораздо больше кода. Мой код теперь обращается к базовым функциям напрямую, а не через xClassB_Object, поэтому я теряю «квалификатор». «Откуда взялась эта функция, которая не имеет ничего общего с функциональностью AppLevel / ClassA?!»

Чтобы попытаться смягчить «двусмысленность», я использую больше уточнений в вызовах. Вместо просто:

ClassB_Action()

Глядя на полный вызов:

this->ClassB_Base::ClassB_Action()

Я удаляю «this->» и использую следующее:

ClassB_Base::ClassB_Action()

/ РЕДАКТ. 1:

Вот более подробная версия вопроса.

У меня есть цепочка классов. Некоторые из них являются рабочими, а некоторые выделяют для рабочих. Это дает наибольшую гибкость при использовании классов.

Мое намерение состоит в том, чтобы пользователь извлекал любой уровень в цепочке без необходимости реализовывать что-либо для поддержки базового класса, кроме вызова конструкторов. Затем используйте функции-члены базового класса напрямую.

Проблема в том, что из-за порядка вызова конструкторов мои объекты не создаются полностью, когда их ссылка передается конструктору базового класса. Я знаю, что могу передать ссылку на этом этапе, но я просто не могу использовать ее в конструкторе базового класса.

Это целая цепочка классов:

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

Примеры использования:

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

1 Ответ

1 голос
/ 07 ноября 2019

В C ++ базовый класс всегда создается до того, как создается любая часть производного класса. Нет исключений, нет обходных путей, нет альтернатив. Никакой части производного класса не существует, пока базовый класс не будет полностью создан, и его конструктор (если таковой имеется) вернется. Это фундаментальный C ++.

Однако

Другая фундаментальная часть C ++ - множественное наследование, и при множественном наследовании все базовые классы всегда создаются впорядок объявления (я игнорирую виртуальное наследование, которое добавляет некоторые дополнительные сложности, которые здесь не актуальны).

Это в значительной степени единственный инструмент, который у вас есть для настройки порядка, в котором создаются вещи. Но это может быть использовано осторожно в ваших интересах. Например, вместо кода, показанного в первом примере:

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

Вместо того, чтобы ClassB закрытый член ClassA, вы наследуетесь от него в частном порядке;а затем передать ссылку на него конструктору ClassA_Base. ClassB сначала создается полностью, затем ClassA_Base получает ссылку на него.

...