简体   繁体   中英

Polymorphism returning “this” of derived, from base class in C++

Having some trouble with polymorphism in C++. I'm trying to create a rather strange syntax for initializing a class but it appears I'm losing the newly created derived class when I return "this" from a base class method.

In the pseudo code below, I need to have Base::initWithPuppies() able to do some default action and return the derived class that it is calling from.

I want to have this syntax:

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

Preferably without using templates and needing to cast like:

initWithPuppies<Bar *>();

Yeah, I know it's rather wack. But there's a reason for it and "best practices" is not applying in this situation. Consider this just a "what if". I know you should probably:

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

But I need the former syntax.

Pseudo code:

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();

EDIT:

If it was somehow possible to have this syntax:

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

That would be even better, but I couldn't figure out how to have create in the base class create a new instance of a derived without typecasting:

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

MY ANSWER: (can't answer my own for 8 hours)

Although Nicol Bolas is correct and as I stated the syntax I want to use is bad practice, if you really do need to use a similar syntax as I do (don't ask...) then you can do this:

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();

I want to have this syntax:

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

OK, stop right there. That is not the syntax you want. Constructors exist in C++ for a good reason, and unless you have a very good reason for circumventing them, you should use them.

If you can't use a constructor for some reason, then use a factory function:

Bar *baz = Bar::initWithPuppies();

It will do the allocation and initialization of the object, so you don't have to use new directly.

As to the reason for the error, it is because you cannot implicitly up-convert. All Bar objects are also Base objects, by the nature of inheritance. Therefore, C++ will implicitly convert pointers to a derived class into pointers to a base class. The reverse is not true: Base classes are not automatically all Bar classes. Therefore, C++ will rightfully give you an error for attempting to convert up the inheritance hierarchy.

You have to explicitly use a dynamic_cast to do this kind of conversion. You could use a C-style cast or a static_cast , but those will only work if you're absolutely certain that the type is what you expect it to be.

Something you may find useful is C++'s support for covariant return types . This means that if in a base class you define a function that returns an object of type Base * , you can override that method to have it return either a Base* or any pointer to a derived class of Base . For example, this code is perfectly legal:

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();

Hope this helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM