2009-09-21 7 views
4

У меня есть базовый класс, который я хочу, чтобы выглядеть следующим образом:Как я могу получить полиморфное поведение в конструкторе C++?

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

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

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

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


на основе this link, казалось бы, что ответ не существует никакого способа, чтобы получить то, что я хочу. Однако то, что он говорит:

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

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

(теперь обратно к чтению этой ссылке)

+0

Читайте это: http://stackoverflow.com/questions/1425695/1426834. Возможно, вы сможете использовать PIMPL. –

ответ

6

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

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

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

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

+0

Вы имели в виду «вы не можете ...»? – Naveen

+0

Да, это была опечатка, спасибо Naveen :) – avakar

+0

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

0

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

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

0

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

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

Что вам нужно, это two-phase construction. Используйте средство Universal Programmer: Добавьте еще один слой косвенности.