Calling the superclass' constuctor twice in the subclass' constructor - c++

I have a user defined class, say A. I have another user defined class B which publicly extends A.
When I define the constructor of B, I have to call the constructor of A in the member initializer list of B's constructor, or else the compiler will call the default constructor.
class A {
int a;
public:
A(int x = 0) {
a = x;
}
};
class B : public A {
int b;
public:
B(int x = 0) : A(2*x) {
b = x;
}
};
Now, I understand the logic behind all this. However, what I want to know is what happens when I do this instead:
class A {
int a;
public:
A(int x = 0) {
a = x;
}
};
class B : public A {
int b;
public:
B(int x = 0) {
A(2*x);
b = x;
}
};
I mean, we all know that the compiler will call the default constructor of A before it enters the body of the B's constructor. Now, the first line of B's constructor body is to call another constructor of A (which takes arguments). So, what does this result in? Two different constructors of the superclass are getting called here whenever an object of the subclass is being created. Please explain what happens...whether two objects will be created or what.

That doesn't call the constructor for the A subobject of the B being initialised. Instead, it creates and destroys a temporary object of type A. The only way to call the constructor of the base subobject is through the initialiser list, as in the first example.

Now, the first line of B's constructor body is to call another constructor of A (which takes arguments). So, what does this result in?
It constructs a temporary A and the temporary object is discarded right away. That call has no relationship to initializing the A part of B.

Related

Why can't I use a class object as a parameter

I have this problem with getting class to pass as a parameter for another class constructor. Basically the code is like this:
class A
{
public:
int ID;
A(int getID)
{
ID = getID;
}
}
and I want to use that class A as a member of class B like this:
class B
{
public:
A someA;
A someB;
int number;
B(A ObjectA, A ObjectB, int getNumber)
{
someA = ObjectA;
someB = ObjectB;
number = getNumber;
}
};
The errors are basically saying that there is no matching function to call B::B(). I don't know whats wrong with it. I have done similar things with vectors of object, so I thought why cant this thing works. Any inputs/correction is appreciated, thank you!
Sidenotes: I have tried adding a default constructor for B as suggested in another thread, but it ended up saying invalid use of B::B.
Use the initialization list for your objects:
B(A ObjectA, A ObjectB, int getNumber)
:someA(std::move(ObjectA)), someB(std::move(ObjectB)), number(getNumber)
{
}
This will use the default move constructor from your class.
I used move here because you are passing your objects by value, so it makes sense to move them. If you passed them by const&, then don't move them and use the default copy constructor.
Still, this is about A default constructor, there is no problem with the B default constructor in the code you showed.
The error happens because you didn't tell B how to initialize its A attributes, so it's looking for the default A constructor.
In C++ the attribute initialization is made as show by Matthieu, and not in the constructor's body. You can add a default A constructor, but you have to think if it's the behavior you want for your code.
Compiling the code below doesn't show any error.
class A {
public:
int ID;
A() {}
A(int getID) {
ID = getID;
}
};
class B {
public:
A someA;
A someB;
int number;
B() {}
B(A ObjectA, A ObjectB, int getNumber) {
someA = ObjectA;
someB = ObjectB;
number = getNumber;
}
};

Call non-default constructor as member initialization

I have a class "A", and a class "B" such that A contains an instance of B
class A
{
B b = B(parameters...);
Other thing = 3;
}
The problem with this code, is that B does not (and should not!) have a copy constructor, so the compiler is complaining
I would like to be able to call B's constructor like below, but it interprets it as a function declaration
class A
{
B b(parameters...);
Other thing = 3;
}
Is there a way to call the non-default constructor in the definition of the class?
Default member initializer (since C++11) only supports brace or equals initializer; you could use brace initializer here.
class A
{
B b{parameters...};
Other thing = 3;
};
If you need to make the copy constructor not visible you can make it private
Class B
{
public:
B(parameters...){};
private:
B(B b){};
}
As for your code i think your problem is that you need to initiate the member in the constructor of A like this:
class A
{
A()
: B(parameters...)
{
thing = 3;
}
B b;
Other thing;
}

Declaration vs. Instantiation

Comming from Java, I have difficulty with the code below.
In my understanding b is just declared on line 3 but not instantiated.
What would be the text book way of creating an instance of B in class A?
class A {
private:
B b;
public:
A() {
//instantiate b here?
}
};
Edit: What if B does not have a default constructor?
You could explicitly initialize b in A's constructor's initialization list, for example
class A {
B b; // private
public:
A : b() {} // the compiler provides the equivalent of this if you don't
};
However, b would get instantiated automatically anyway. The above makes sense if you need to build a B with a non-default constructor, or if B cannot be default initialized:
class A {
B b; // private
public:
A : b(someParam) {}
};
It may be impossible to correctly initialize in the constructor's initialization list, in which case an assignment can be done in the body of the constructor:
class A {
B b; // private
public:
A {
b = somethingComplicated...; // assigns new value to default constructed B.
}
};
You have created an instance of b in line 3. This line is enough so that B's constructor is called. If you have code like this
class A {
private:
B *b;
public:
A() {
//instantiate b here?
}
};
then it would make sense to instantiate b in A's constructor like
A()
{
b = new B();
}
The correct phase your looking for is "C++ initialization list". This initialization list is called/initialized before the constructor is called
In case of Default constructor, compiler equvalient constructor will be A() : B() {}
A very good reference
http://www.cprogramming.com/tutorial/initialization-lists-c++.html
At line 3, it is simply a declaration of B. However somewhere in your code where you have:
A a;
or
A a();
This calls the constructor of A. The internal b private member is full or garbage, as in not initialized. You are correct in that you can and probably should initialize member variable during construction where possible. There are two ways to do this:
A ()
{
b = B ();
}
Like you said:
or
A () : b (B())
{
}
The second version (initialization list) is slightly more efficient since it creates the new B object directly inside b. Whereas the first version creates a temporary and then moves that into b. This is the case when you initialize members from passed in parameters anyway (for non built in types). I'm making an assumption its the same in this case, but someone will be able to clarify.

OOP Constructor question C++

Let's say that I have two classes A and B.
class A
{
private:
int value;
public:
A(int v)
{
value = v;
}
};
class B
{
private:
A value;
public:
B()
{
// Here's my problem
}
}
I guess it's something basic but I don't know how to call A's constructor.
Also the compiler demands a default constructor for class A. But if A has a default constructor than wouldn't the default constructor be called whenever I declare a variable of type A. Can I still call a constructor after the default constructor has been called? Or can I declare an instance of a class and then call a constructor later?
I think this could be solved using pointers but can that be avoided ?
I know that you can do something like this in C#.
EDIT: I want to do some computations in the constructor and than initialize the classes. I don't know the values beforehand.
Let's say I'm reading the value from a file and then I initialize A accordingly.
The term you are looking for is initializer list. It is separated from the constructor body with a colon and determines how the members are initialized. Always prefer initialization to assignment when possible.
class A
{
int value;
public:
A(int value) : value(value) {}
};
class B
{
A a;
public:
B(int value) : a(value) {}
}
I want to do some computations in the constructor and than initialize the classes. I don't know the values beforehand.
Simply perform the computation in a separate function which you then call in the initializer list.
class B
{
A a;
static int some_computation()
{
return 42;
}
public:
B() : a(some_computation()) {}
}
You use an initialization list to initialize the member variables.
public:
B() : value(4) { // calls A::A(4) for value.
}
Or can I declare an instance of a class and then call a constructor later?
Yes, you can do that. Instead of (A value;) declare (A* value;), and then B's constructor will be B():value(new A(5)){}.
In the B's destructor you will have to do delete value;
I think this could be solved using pointers but can that be avoided ?
Yes. Use shared_ptr.
Try:
B() :
value(0)
{
// Here's my problem
}

Constructor initialising an array of subobjects?

Say I have several objects within a class, each of which needs constructing with a different value. I can write something like this:
class b
{
public:
b(int num)
{
// 1 for a.b1, and 2 for a.b2
}
};
class a
{
public:
b b1;
b b2;
a() : b1(1), b2(2)
{
}
};
However, is it possible to do the same thing if those multiple objects are stored in an array?
My first attempt at it doesn't compile:
class a
{
public:
b bb[2];
a() : bb[0](1), bb[1](2)
{
}
};
You cannot do this directly; you need to initialize the array elements in the body of the constructor.
The elements of the array are default constructed before the body of the constructor is entered. Since your example class b is not default constructible (i.e., it has no constructor that can be called with zero parameters), you can't have an array of b as a member variable.
You can have an array of a type that is not default constructible in other contexts, when you can explicitly initialize the array.