Как бы я перевел следующий механизм "ссылки на класс" с Delphi на C++11?

Некоторое время я использовал C++, и иногда меня беспокоит то, что я до сих пор не могу понять, как сделать эту конструкцию Delphi Factory в C++.

Ключевая часть, которую я не могу понять, это как передать ссылку на тип класса в C++. В Delphi у нас есть тип "TClass". Переменные этого типа являются ссылкой на некоторый класс. Мы можем ограничить классы, на которые может ссылаться ссылка на класс, используя class of MyClass синтаксис для определения нового типа ссылки на класс.

Обратите внимание, что термин "ссылка на класс Delphi" =/= "экземпляр класса C++"

Для тех, кто не знаком с Паскалем, объявлены переменные variable: type а не в стиле C type variable, Аналогично, тип возврата функции появляется после списка параметров и имени.

Я сократил синтаксис в следующем примере, чтобы сделать его менее наглядным, поэтому прошу прощения у разработчиков Delphi за ужасное форматирование. Ключевые части описаны в комментариях.

program demo;

{$APPTYPE CONSOLE}

// declarations

type

  Base = class
  public constructor Create(name: string); virtual;
  end;

  // Class reference which refers to Base and its descendants
  BaseClass = class of Base;

  ChildA = class(Base)
  public constructor Create(name: string); override;
  end;

  ChildB = class(Base)
  public constructor Create(name: string); override;
  end;

// implementation

constructor Base.Create(name: string);
begin
  WriteLn('Base says hi to ' + name);
end;

constructor ChildA.Create(name: string);
begin
  inherited Create(name);
  WriteLn('ChildA says hi to ' + name);
end;

constructor ChildB.Create(name: string);
begin
  WriteLn('ChildB says hi to ' + name);
end;

// *** THIS IS THE BIT THAT I'M INTERESTED IN ***
// The function doesn't know at compile time exactly what class it is being
// asked to construct.  The compiler knows that it is or inherits from Base.
// I can't find any kind of "class reference" in C++.
function ConstructSomething(ClassType: BaseClass; name: string): Base;
begin
  Result := ClassType.Create(name);
end;

// Equivalent to "main()" in C
begin

  // Pass references to a class to the ConstructSomething function
  ConstructSomething(Base, 'Mark');
  WriteLn('');

  ConstructSomething(ChildA, 'Mark');
  WriteLn('');

  ConstructSomething(ChildB, 'Mark');
  WriteLn('');

end.

Выход:

Base says hi to Mark

Base says hi to Mark
ChildA says hi to Mark

ChildB says hi to Mark

Обратите внимание, что при передаче ссылки на дочерний класс создается дочерний класс. Не вызывать базовый конструктор в ChildB намеренно в этой демонстрации, чтобы сделать концепцию "ссылки на класс" немного более очевидной.

2 ответа

Решение

Самое близкое, что вы можете получить, это использовать функцию шаблона.

template<typename ClassType>
Base* ConstructSomething(string name)
{
   return new ClassType(name);
}

int main()
{
    ConstructSomething<Base>("Mark");
    ConstructSomething<ChildA>("Mark");
}

но ClassType не может быть выбран во время выполнения - это должно быть известно во время компиляции.

C++ не имеет ничего общего со встроенными ссылками класса Delphi. И ссылки на классы - это нечто большее, чем просто виртуальные конструкторы; Вы также можете определить (виртуальные и не виртуальные) методы класса, что очень удобно.

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

// some central header

template <typename Base>
  class Metaclass;


// your header

class Base
{
public:
  Base (const String& name) { /*...*/ }
};
template<>
  class Metaclass<Base>
{
public:
  virtual Base* create (const String& name);
};
extern Metaclass<Base> classof_Base;

class ChildA : public Base
{
public:
  ChildA (const String& name) { /*...*/ }
};
template<>
  class Metaclass<ChildA> : public Metaclass<Base>
{
public:
  // note how I'm making use of C++'s support for covariance
  ChildA* create (const String& name) override;
};
extern Metaclass<ChildA> classof_ChildA;

class ChildB : public MyBase
{
public:
  ChildB (const String& name) { /*...*/ }
};
template<>
  class Metaclass<ChildB> : public Metaclass<Base>
{
public:
  ChildB* create (const String& name) override;
};
extern Metaclass<ChildB> classof_ChildB;


// your source file

Metaclass<Base> classof_Base;
Base* Metaclass<Base>::create (const String& name)
{
  return new Base (name);
}

Metaclass<ChildA> classof_ChildA;
MyBase* Metaclass<ChildA>::create (const String& name)
{
  return new ChildA (name);
}

Metaclass<ChildB> classof_ChildB;
MyBase* Metaclass<ChildB>::create (const String& name)
{
  return new ChildB (name);
}

Теперь вы можете скопировать свой код Delphi на C++:

Base* constructSomething (Metaclass<Base>& classType, const String& name)
{
  return classType.create (name);
}

int main (void)
{
  constructSomething (classof_Base, "Mark");
  std::sprintf ("\n");
  constructSomething (classof_ChildA, "Mark");
  std::sprintf ("\n");
  constructSomething (classof_ChildB, "Mark");
  std::sprintf ("\n");
}
Другие вопросы по тегам