1

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.

kian
  • 53
  • 1
  • 7
  • "I'm converting some Java code to C++ and I want to keep the class structure similar." That's a bad idea to begin with. – D Drmmr Nov 24 '14 at 16:01
  • 1
    Java is not C++, and in general, not a good idea to write C++ code by leveraging what you know about a similar looking language. Just because one looks like the other doesn't mean you can willy-nilly make line-for-line coding changes. As you can see from the answer, the rules are different. You are also missing a virtual destructor for your base class, let alone have memory leaks in your example (using `new` in C++ is not the same as Java). – PaulMcKenzie Nov 24 '14 at 16:09

5 Answers5

5

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.

Community
  • 1
  • 1
sfjac
  • 7,119
  • 5
  • 45
  • 69
3

AFAIK in Java it's only bad style to call a (non-final) method in the constructor, i.e., 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?

Robert
  • 7,394
  • 40
  • 45
  • 64
2

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();
}
Barry
  • 286,269
  • 29
  • 621
  • 977
  • To nit-pick, regarding "the derived class' vtable won't have been constructed yet" - that's typically done once per class by the compiler; the issue is that the object being constructed won't be linked to (pointed at) that derived-class vtable yet. – Tony Delroy Nov 24 '14 at 16:05
  • @TonyD What's the better word... updated? assigned correctly? – Barry Nov 24 '14 at 16:09
  • "linked to" or "known to" perhaps, but it's hard to make it read well while keeping the same general phrasing. I doubt the OP will know about vtables too. I'm not good at this, and trying to do a thorough job ended up with this: "...base class constructor; the base class subobject typically holds a pointer to a table of function pointers (Virtual Dispatch Table, VDT) that implement the virtual functions, but while the base class constructor runs its the table of base class constructor runs the object under construction will point to the base class VDT; ... – Tony Delroy Nov 24 '14 at 16:22
  • ...it's the derived class constructor that replaces that pointer with a pointer to it's own VDT when the derived class object's other bases and members are constructed and it's therefore safe to operate on the object as an instance of the derived type." Use or hack around as you like, or let people read it here in comments if easier.... Cheers. – Tony Delroy Nov 24 '14 at 16:22
  • @TonyD I like "linked to", we'll go with that :) – Barry Nov 24 '14 at 16:25
  • Hehe ;-) Fair enough...! – Tony Delroy Nov 24 '14 at 16:27
  • 1
    At least the C++ behavior seems sane - during construction of the base class, the object is-a base object. Still smelly to call a virtual function but at least it seems internally consistent and occasionally useful. The Java behavior is a recipe for undefined behavior. – sfjac Nov 24 '14 at 17:07
0

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).

Sean
  • 60,939
  • 11
  • 97
  • 136
0

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

Morad
  • 2,761
  • 21
  • 29