Можно ли создать экземпляр класса внутри самого класса?
скажем у меня есть класс:
class Foo{
public:
Foo(){
}
//Is it possible to create a function like this:
virtual Foo* createOb(){
//Should create a new Foo,Bar or Fiz, depending on the actual object type.
}
}
class Bar: public Foo{
public:
Bar(){
}
}
class Fiz: public Foo{
public:
Fiz(){
}
}
возможно ли иметь метод createOb()
в базовом классе, поэтому, когда createOb() вызывается на экземпляре одного из производных классов, создается экземпляр производного класса ?
4 ответов
Да, это можно сделать, используя CRTP.
Bu во-первых, возврат необработанного указателя, полученного из new
и очень опасно. В c++
сырые указатели следует использовать только тогда, когда они не имеют права собственности на указанный объект. Поэтому я взял на себя смелость использовать unique_ptr
:
struct Base {
virtual auto create_obj() -> std::unique_ptr<Base>
{
return std::unique_ptr<Base>{};
}
};
// abstract works too:
struct Base {
virtual auto create_obj() -> std::unique_ptr<Base> = 0;
};
template <class Derived>
struct Base_crtp : Base {
auto create_obj() -> std::unique_ptr<Base> override /* final */
{
return std::unique_ptr<Base>{new Derived{}};
}
};
struct D1 : Base_crtp<D1>
{
};
struct D2 : Base_crtp<D2>
{
};
и затем:
auto b1 = std::unique_ptr<Base>{new D1{}};
auto b2 = std::unique_ptr<Base>{new D2{}};
auto new_d1 = b1->create_obj();
auto new_d2 = b2->create_obj();
определенно да!!!
когда метод объявляется виртуальным в базовом классе и вызывается через объект производного класса, вызывается функция производного класса (чтение vprt, vtable concept В C++).
#include <iostream>
using namespace std;
class A{
public:
virtual A* getobj(){
return new A();
}
};
class B: public A{
public:
B(){cout<<"B constructor"<<endl;}
virtual A* getobj(){
return new B();
}
};
int main()
{
A *a = new B();
A *second = a->getobj();
return 0;
}
в приведенном выше коде мы вызываем функцию getobj (), используя объект класса B. Здесь конструктор класса B вызывается дважды.
во-первых, для нового B () в main
secondly для функции getobj вызов, который снова создает объект B
это не оптимальное решение, но оно работает.
в вашей .h
class Foo{
public:
Foo();
virtual Foo* createOb();
};
class Bar: public Foo{
public:
Bar();
};
class Fiz: public Foo{
public:
Fiz();
};
в вашей .cpp
#include "Header.h"
Foo::Foo() {}
Foo* Foo::createOb(){
if (dynamic_cast<Bar*>(this)) {
return new Bar();
}
else if (dynamic_cast<Foo*>(this)) {
return new Foo();
}
return nullptr;
}
Bar::Bar() {}
Fiz::Fiz() {}
Как уже предлагалось, пожалуйста, рассмотрите чистый виртуальный метод
нет, это невозможно с" чистым " наследованием. Классы должны переопределить createOb()
функция-член для поддержки клонирования.
вы можете понять, почему это невозможно, рассматривая отдельную компиляцию классов. Реализация one-fits-all createOb()
функция-член должна быть завершена в изоляции от Bar
и Fiz
, что делает невозможным для базы знать тип ее подклассов.
реализация с чистой виртуальной функцией в однако база очень распространена.
другой подход заключается в использовании любопытно повторяющийся шаблон шаблона (CRTP) для осуществления клонирования. Это статьи объясняет, как это можно сделать.