简体   繁体   中英

Polymorphism in C++ vs Java

I'm converting some Java code to C++ and I want to keep the class structure similar. However, I have encountered the following problem, which I don't know how to solve; I do this in Java:

public class Mother {   
    protected Father make;  
    public  Mother(){       
        make = maker();         
        make.print(); };    
    public Father maker(){ 
        return new Father();};}

public class Daughter extends Mother {
    public Daughter(){
        super();}
    @Override
    public Father maker(){
        return new Son();};}

public class Father {
    public void print(){
        System.out.println("I am the Father!\n");}}

public class Son extends Father {
    @Override
    public void print(){
        System.out.println("I am the son!\n");};}

public static void main(String[] args) {
    Daughter dot  = new Daughter();
}

will produce: I am the son! While:

class father{
public:
    virtual void print(){
        std::cout << "I am the father!\n";
}; };

class son: public father{
public:
    virtual void print(){
        std::cout << "I am the son!\n";
    };};

class mother{
protected:
    father *make;
public:
    mother(){
        make = maker();
        make->print();
    };
    virtual father *maker(){
        return new father();
    };};

class daughter: public mother{
public:
    daughter(): mother() {
    };
    virtual father *maker(){
        return new son();
    };};


int main(int argc, const char * argv[]) {
    daughter *d = new daughter();

will produce I am the father! . How can I make the C++ code to produce the same result as the Java code? Thanks.

Daughter 's constructor invokes the Mother constructor, which invokes maker() . In C++, at least, the object is only considered to be a Mother at this point as the Daughter construction is incomplete. Thus Mother::maker() is invoked, so this is doing the correct thing. However, it is generally considered a strong code smell to invoke virtual functions during construction - for exactly these sorts of reasons.

In Java, apparently the sub-class overrides are always called, even during construction, and as a result constructors in Java should never call overridable methods. Doing so can lead to undefined behavior. There is a very nice explanation of this here .

AFAIK in Java it's only bad style to call a (non-final) method in the constructor, ie, a method which can be overridden in a derived class. C++ does always call the actual class' version, not the overridden one.

Can you get around this by passing the object to the constructor?

You shouldn't call virtual functions from the base class constructor - the derived class' vtable won't have been linked to yet, so you'll always end calling the base class' function. You shouldn't really do this in Java either as while it will call the correct function, the most derived class won't have been instantiated yet - which could lead to undefined behavior. Ultimately, it's wrong in both languages for different reasons.

One way around this is to have the derived class pass the result of the would-be-virtual call into the base class:

daughter(): mother(new son) { }

So that:

mother() : make(new father) { make->print(); }
mother(father * m) : make(m) { make->print(); }

This becomes easier with delegating constructors:

mother()
: mother(new father)
{ }

mother(father* m)
: make(m)
{
    make->print();
}

In C++ calling a virtual function from a base constructor doesn't call the more derived implementation. The reason for this is that with a constructor for type BASE the type is BASE , even if the constructor is being called from a derived class, DERIVED . Because of this the virtual function table is still being constructed and won't point to the more derived implementation until after the DERIVED constructor has finished executing.

Java (and C#) differ from C++ here as you can call a virtual function from a base constructor, and it will call the most derived implementation. However, as the most derived constructor won't have run yet the object may be in an undefined state, and this is why it' not recommended to call virtual functions from constructors.

As to how to get around it, you could add an initialize method which you call after creating your instance. As the object will be fully constructed at this point it will call the correct virtual function (in all languages).

When you create a Derived object, it first calls Base's constructor. When executing the Base's constructor (Mother), the this object is not yet of type Derived(daughter); its type is still merely Base (Mother) Read this for more information: http://www.parashift.com/c%2B%2B-faq-lite/calling-virtuals-from-ctors.html

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