Можно ли создать экземпляр класса внутри самого класса?

скажем у меня есть класс:

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 вызывается дважды.

  1. во-первых, для нового B () в main

  2. 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) для осуществления клонирования. Это статьи объясняет, как это можно сделать.