Как я могу получить полиморфное поведение в конструкторе C ++? - PullRequest
5 голосов
/ 21 сентября 2009

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

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

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

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

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


Исходя из этой ссылки может показаться, что ответ - нет способа получить то, что я хочу. Однако, это говорит:

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

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

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

Ответы [ 4 ]

7 голосов
/ 21 сентября 2009

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

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

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

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

0 голосов
/ 21 сентября 2009

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

0 голосов
/ 21 сентября 2009

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

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()
    {
    }
};
0 голосов
/ 21 сентября 2009

Возможно, использовать статический фабричный метод для каждого производного типа? Это обычный способ конструирования экзотических объектов (читай: объектов с очень специфическими требованиями инициализации) в .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);
  }
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...