Как я могу получить полиморфное поведение в конструкторе C++?

StackOverflow https://stackoverflow.com/questions/1453131

  •  12-09-2019
  •  | 
  •  

Вопрос

У меня есть базовый класс, который я хочу выглядеть так:

class B
{
    // should look like: int I() { return someConst; }
    virtual int I() = 0;
    public B() { something(I()); }
}

Дело в том, чтобы заставить производные классы переопределить I и заставить его вызываться при создании каждого объекта.Это используется для ведения некоторой бухгалтерии, и мне нужно знать, какой тип объекта создается (но в противном случае я рассматриваю текущий объект как базовый класс).

Это не работает, поскольку C++ не позволяет вызывать абстрактную виртуальную функцию из конструктора.

Есть ли способ получить тот же эффект?


На основе эта ссылка казалось бы, ответ таков: нет способа получить то, что я хочу.Однако там написано:

Краткий ответ:нет.Базовый класс ничего не знает о том, от какого класса он произошел, и это тоже хорошо. [...] То есть объект официально не становится экземпляром Derived1, пока не запустится конструктор Derived1::Derived1.

Однако в моем случае я не хочу знать, что это такое. является но что это будет становиться.На самом деле, меня даже не волнует, что я получу в ответ, если я, пользователь, могу (постфактум) сопоставить это с классом.Так что я мог бы даже использовать что-то вроде указателя возврата, и это сошло бы мне с рук.

(теперь вернемся к чтению этой ссылки)

Это было полезно?

Решение

Вы не можете вызывать виртуальные методы из конструктора (точнее, вы может вызовите их, но в конечном итоге вы вызовете функцию-член из конструируемого в данный момент класса). Проблема в том, что производный объект в этот момент еще не существует.С этим мало что можно поделать: полиморфный вызов виртуальных методов из конструктора просто исключен.

Вам следует переосмыслить свой дизайн - например, передать константу в качестве аргумента конструктору.

class B
{
public:
    explicit B(int i)
    {
        something(i);
    }
};

Видеть Часто задаваемые вопросы по C++ для большего.Если вы Действительно хотите вызывать виртуальные функции во время построения, прочитай это.

Другие советы

Возможно, использовать статический фабричный метод для каждого производного типа?Это обычный способ создания экзотических объектов (читай:те, у кого очень специфические требования к инициализации) в .NET, что я оценил.

class Base
{
  protected Base(int i)
  {
    // do stuff with i
  }
}

class Derived : public Base
{
  private Derived(int i)
    : Base(i)
  {
  }

  public Derived Create()
  {
    return new Derived(someConstantForThisDerivedType);
  }
}

Вызов виртуальных методов в базовых конструкторах обычно не одобряется, поскольку вы никогда не можете быть уверены в поведении конкретного метода, и (как уже отмечал кто-то другой) производные конструкторы еще не были вызваны.

Это не сработает, поскольку производный класс еще не существует на момент выполнения конструктора базового класса:

class Base
{
public:
    Base()
    {
        // Will call Base::I and not Derived::I because
        // Derived does not yet exist.
        something(I());
    }

    virtual ~Base() = 0
    {
    }

    virtual int I() const = 0;
};

class Derived : public Base
{
public:
    Derived()
     : Base()
    {
    }

    virtual ~Derived()
    {
    }

    virtual int I() const
    {
        return 42;
    }
};

Вместо этого вы можете добавить аргументы в конструктор базового класса:

class Base
{
public:
    explicit Base(int i)
    {
        something(i);
    }

    virtual ~Base() = 0
    {
    }
};

class Derived : public Base
{
public:
    Derived()
     : Base(42)
    {
    }

    virtual ~Derived()
    {
    }
};

Или, если вам действительно нравится ООП, вы также можете создать пару дополнительных классов:

class Base
{
public:
    class BaseConstructorArgs
    {
    public:
        virtual ~BaseConstructorArgs() = 0
        {
        }

        virtual int I() const = 0;
    };

    explicit Base(const BaseConstructorArgs& args)
    {
        something(args.I());
    }

    virtual ~Base() = 0
    {
    }
};

class Derived : public Base
{
public:
    class DerivedConstructorArgs : public BaseConstructorArgs
    {
    public:
        virtual ~DerivedConstructorArgs()
        {
        }

        virtual int I() const
        {
            return 42;
        }
    };

    Derived()
     : Base(DerivedConstructorArgs())
    {
    }

    virtual ~Derived()
    {
    }
};

Что вам нужно, это двухэтапное строительство.Используйте лекарство универсального программиста:Добавьте еще один уровень косвенности.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top