Полиморфизм, возвращающий «this» производного от базового класса в C ++ - PullRequest
0 голосов
/ 08 сентября 2011

Возникли проблемы с полиморфизмом в C ++.Я пытаюсь создать довольно странный синтаксис для инициализации класса, но, похоже, я теряю вновь созданный производный класс, когда возвращаю «this» из метода базового класса.

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

Я хочу иметь следующий синтаксис:

Bar *baz = (new Bar)->initWithPuppies();

Предпочтительно без с использованием шаблонов и необходимостью разыгрывать как:

initWithPuppies<Bar *>();

Да, я знаю, что это довольно странно.Но есть причина для этого, и «лучшие практики» не применяются в этой ситуации.Считайте, что это просто «что если».Я знаю, что вы, вероятно, должны:

Bar *baz = new Bar;
baz->initWithPuppies();

Но мне нужен прежний синтаксис.

Псевдокод:

class Base
{
    // Kittens
};

class Foo : public Base
{
  public:
    Base * initWithPuppies();
    virtual void test() = 0;
};

Base * Foo::initWithPuppies()
{
    // Call to derived works
    this->test();

    return this;
}

class Bar : public Foo
{
  public:
    void test();
};

void Bar::test()
{
    std::cout << "It Works!" << std::endl;
}

// Preferred syntax
// This gives "cannot convert from 'Base *' to 'Bar *' "
Bar *baz = (new Bar)->initWithPuppies();

baz->test();

/*------------------------------------------*/

// This gives " 'test' : is not a member of 'Base' "
Base *baz = (new Bar)->initWithPuppies();

baz->test();

/*------------------------------------------*/

// This gives "Base is not a polymorphic type"
UIBar *man = dynamic_cast<UIBar *>((new UIBar)->initWithFrame());

baz->test();

РЕДАКТИРОВАТЬ:

Если бы было так или иначе возможно иметь этот синтаксис:

Bar *baz = (Bar::create())->initWithPuppies();

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

Bar *baz = (Bar::create<Bar *>())->initWithPuppies();

МОЙ ОТВЕТ: (не могу ответить на свой в течение 8 часов)

Хотя Николь Болас прав и, как я сказал, синтаксисЯ хочу использовать это плохую практику, если вам действительно нужно использовать такой же синтаксис, как я (не спрашивайте ...), тогда вы можете сделать это:

class Base
{
    // Kittens
};

class Foo : public Base
{
  public:
    virtual void test() = 0;
  private:
    void _initWithPuppies();
};

void Foo::initWithPuppies()
{
    // Do shit
}

class Bar : public Foo
{
  public:
    Bar * initWithPuppies();
    void test();
};

Bar * Bar::initWithPuppies()
{
    this->_initWithPuppies();

    return this;    
}

void Bar::test()
{
    std::cout << "It Works!" << std::endl;
}


Bar *baz = (new Bar)->initWithPuppies();

baz->test();

Ответы [ 2 ]

9 голосов
/ 08 сентября 2011

Я хочу использовать этот синтаксис:

Bar *baz = (new Bar)->initWithPuppies();

ОК, остановитесь прямо здесь. Это не тот синтаксис, который вы хотите. Конструкторы существуют в C ++ по уважительной причине, и если у вас нет веских причин для их обхода, вы должны их использовать.

Если по какой-то причине вы не можете использовать конструктор, используйте фабричную функцию:

Bar *baz = Bar::initWithPuppies();

Он выполнит выделение и инициализацию объекта, поэтому вам не нужно напрямую использовать new.

Что касается причины ошибки, то это потому, что вы не можете неявно преобразовать с повышением частоты. Все Bar объекты также являются Base объектами по природе наследования. Следовательно, C ++ будет неявно преобразовывать указатели на производный класс в указатели на базовый класс. Обратное значение не true: Base классы не , автоматически все Bar классы. Поэтому C ++ по праву выдаст вам ошибку при попытке преобразовать иерархию наследования.

Вы должны явно использовать dynamic_cast, чтобы сделать этот тип преобразования. Вы можете использовать приведение в стиле C или static_cast, но они сработают, только если вы абсолютно уверены , что тип соответствует ожидаемому.

3 голосов
/ 08 сентября 2011

Что-то, что вы можете найти полезным, это поддержка C ++ для ковариантных типов возврата .Это означает, что если в базовом классе вы определяете функцию, которая возвращает объект типа Base *, вы можете переопределить этот метод, чтобы он возвращал либо Base*, либо любой указатель на производный класс Base.Например, этот код является абсолютно законным:

class Base {
public:
    virtual ~Base() {} // Polymorphic classes need virtual destructors!
    virtual Base* initWithPuppies() = 0;
}

class Derived: public Base {
public:
    /* Note that the return type is Derived*, but it's still an override! */
    virtual Derived* initWithPuppies() {
        return this;
    }
}

/* Perfectly legal code; Derived::initWithPuppies() returns a Derived* */
Derived* d = (new Derived)->initWithPuppies();

Надеюсь, это поможет!

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