Как бы я перевел следующий механизм "ссылки на класс" с 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");
}