Why Does This Call the Default Constructor

calling the default constructor

The second one is declaring a function a() that returns a base object. :-)

Why is this attempting to call the default constructor?

how can I produce the intended behavior (construct member after other initialization code in the constructor)?

This is not possible. In C++, all the object's base classes and non-static member variables must be constructed before the body of the constructor is entered.

In your code you wrote the opening { of the Two constructor without having previously offered any constructor arguments for the member variable, so the member variable is default-constructed.


If you have some logic to work out the initializer for one then I would recommend putting that into a function:

int f() { /* logic here */ return 0; }

// ...

Two(): one( f() ) {}

Of course you could also add a default constructor to One plus a means of assigning to it later.

when are constructors called and how to not call them

This happens because your B class contains a member of type A. As part of the construction of B, the B::object member is constructed, which requires a call to the A constructor. So you actually have two A instances: object and bObject.object.

Because you did not put object in B::B()'s initializer list, B::object is default-constructed. object = aObject; then assigns to the existing A stored in object. So rather than copy-constructing B::object, you default-construct it (which is responsible for the second "hello" you see) and then you assign a new value to it.

The compiler will provide a copy-constructor A(const A &) for you, so you can copy-construct B::object from the parameter like so:

B(A aObject) : object(aObject) { }

This constructor does not call the default constructor, nor does it contain the code contained in the default constructor. Therefore, calling this constructor does not cause output of "hello".

Note that you would still be constructing a new A (in fact you would be doing so twice, because aObject is passed by value) but since you did not define the copy-constructor yourself, it wouldn't contain your code that writes a string to std::cout.

The actual sequence of relevant events that happen here is:

  1. A object; calls the A::A() constructor. (The first "hello" is printed from here.)
  2. B bObject(object); will call the B::B(A) constructor, but because the A argument is accepted by value, object is first copied into a new, temporary A using the compiler-generated A::A(const A &) copy constructor.
  3. The B::B(A) constructor is called to create bObject, passing in the temporary A constructed in the last step.
  4. Inside of the B constructor, the B::object object is default-constructed using A::A() because it was not in the constructor's initializer list. (The second "hello" is printed from here.)
  5. Finally, the body of the B::B(A) constructor is run, which assigns the value of the temporary A object (stored in aObject) to the A contained in B::object, which was constructed in the prior step. This assignment is accomplished by using the compiler-generated A & A::operator=(A const &) operator.

Why does a move constructor require a default constructor for its members?

Use the constructor's initializer list to initialize the A member. As written, the move constructor uses, as the compiler says, the default constructor for A.

B(B&& b) : a(std::move(b.a)) {}

NOT wanting to call the default constructor from child class

The question why the superclass constructor is called, even without explicit super(); call is answered here. One of the constructors of the superclass must always be called. If the superclass has a (possibly implicit) default constructor then the super(); call from the subclass can be omitted, but the constructor is called implicitly. Your Cow constructor is the same as this one:

public Cow() {
// Calls the `Animal()` superclass constructor
super();
System.out.println("I am a cow");
}

In your specific situation it might make sense to add an additional Animal constructor which takes the name and sound values as parameters and initializes the fields. If you still need a default Animal() constructor, you can have that delegate to the other constructor:

class Animal {
public String name;
public String sound;

public Animal(String name, String sound) {
// `this.name` refers to the field, `name` refers to the parameter
this.name = name;
this.sound = sound;
}

public Animal() {
// Uses `this(...)` to call the other constructor
this(
"animal",
"an animal makes a sound based on what animal that it is"
);
System.out.println("I am an animal");
}

...
}

Note: If you don't expect these fields to be reassigned, then you should make them final to protect against bugs and for thread-safety improvements.

Your subclasses can then call the Animal(String, String) constructor:

public class Cow extends Animal {
public Cow() {
// Calls the `Animal(String, String)` superclass constructor
super("Cow", "Moo");
}
}

This does not declare additional name and sound fields because the ones from Animal are inherited and accessible to Cow. Your current code for Cow actually introduces additional fields which hide the ones from Animal, see this answer.



Related Topics



Leave a reply



Submit