C++ initializer list members still calling default constructor? - c++

I'm having a problem initializing a shared_ptr member of a class I'm working on.
I have two classes, A and B:
class A {
int _x;
int _y;
public:
A(int, int);
};
A::A(int x, int y) {
_x = x;
_y = y;
}
class B : public A {
std::shared_ptr<A> _a;
public:
B(std::shared_ptr<A>);
};
B::B(std::shared_ptr<A> a) : _a(a) {}
int main() {
std::shared_ptr<A> a = std::make_shared<A>(1, 2);
B b(a);
return 0;
}
I just want class B to hold a std::shared_ptr to an instance of class A.
However, I'm getting the error no matching function for call to A::A() in B's constructor.
I'm confused because I thought the point of the initializer list is to avoid implicitly calling the default constructor for member variables, but it still seems to be trying to call A's default constructor.
Any explanation is appreciated, thanks.
edit: after more messing around, it seems like it complies properly if B does not inherit from A. Still unsure why inheriting from A results in A's default constructor being called from B's constructor.

Since B is derived from A, every B is an A. That means to construct a B, you must also construct an A. However, you do not tell B's constructor how to construct an A, so the default constructor is used.
If you want B to be derived from A and only hold a shared_ptr to A, then A must not do anything you don't want B to do. Here, A has _x and _y.
It's not clear what you really want, but perhaps you want some other base class that both A and B derive from that only has what both A and B should have.
The classic example of inheritance in C++ is something like Instrument being the base class that has members like Play with derived classes like Trumpet and Clarinet having the thing that makes some particular instrument a trumpet. Only what is common to Trumpets and Clarinets should be in Instrument.

When you call B b(a);, you construct a B object using the constructor B::B(std::shared_ptr<A> a).
The issue is in your definition of this constructor. B inherits from A, meaning that B is not just a B, but instead is really an A and B stacked on top of each other. A is the base class, so it is on the bottom. So when you try to construct a B object, the code must first construct the A object at the bottom/base to form the foundation for the B object to exist on. That means that you actually have to tell the code how to construct the A object.
Because you defined a constructor for A that takes two int variables, the compiler does not generate a default constructor for A. Therefore, you must explicitly write out how A should be constructed within B. Constructing the A object can be done in the initializer list of the B object's constructor:
B::B(std::shared_ptr<A> a) : A(0,0), _a(a) {}

Related

How to prevent C++ from creating default members?

Suppose I have the following class:
class A{
...
B obj;
C obj2
...
}
Upon construction of an A instance, B obj would be initialized by the default constructor. But before I can construct obj and obj2, I need to do some computation in the constructor of A and then call a non-default constructor for B obj and C obj2.
It does not cost me a lot, but the call to a default constructor for B obj and C obj2 upon construction of A would be completely unnecessary.
Can I prevent C++ from calling a default constructor? Or is this the wrong approach anyways?
EDIT: For clarification, I added a second object. I have to read from a file and can then construct B and C.
It is the somewhat wrong approach. I would suggest something like this:
int arguments(){ //assuming obj takes an int as 3rd constructor argument, could be any
//do computation you wanted to do in A::A
}
class A{
A() : obj(non, Default, arguments()){}
B obj;
}
Now the initialization is done before obj is created. You may also make arguments return a B and rely on the move constructor of B.
Edit: The answer does not change much with the edited question. Now you have two objects but the same logic applies:
class A{
A() : obj(doInitCalculations()), obj2(something){}
B obj;
C obj2;
}
obj must be initialized before obj2 (even if you write A() : obj2(), obj1{}) because they are constructed in the order they are declared in the class. So doInitCalculations is still called before either object is constructed.
You may delegate your constructor, something like:
class A
{
public:
A() : A(ComputeSomethingForBAndC()) {}
private:
A(const DataToBuildBAndC& dataToBuildBAndC) :
b(dataToBuildBAndC),
c(dataToBuildBAndC)
{}
private:
B b;
C c;
};
No matter what you do, the constructor of B will be called
before you enter into the body of A. If you don't specify
anything in the initializer list, the default constructor will
be called. If you need to calculate values first, then the only
real solution would be to do so in a static member function:
class A
{
B obj;
static B initializeB( ... );
public:
A( ... ) : obj( initializeB( ... ) ) {}
};
This is fine if the calculation only depends on the arguments,
and is only needed for the initialization of B. Otherwise, it
might require doing the same calculations twice.
If the initialization of B depends on other members, which are
also calculated, then you can put the other members before obj
(so they will be initialized first), and use the static member
trick on them. (Just be sure to put a big comment to the effect
that your code absolutely depends on the order of the members.)
Finally, if nothing else works, you can arrange for a "trivial"
constructor for B, which only does the minimum so that an
assignment later will work. Then you can write:
A::A()
: obj( doNothing )
{
// all the calculation
obj = B(...);
}
(For doNothing, just use an enum:
enum DoNothing { doNothing };
The provide an overload B::B( DoNothing ) in B.)
I'd consider this a last resort, as it involves modifying B,
which is rather invasive. Still, I've used it one or two times in the past.
You could use a (smart) pointer for B.
You'll need a level of indirection, something like:
class A {
unique_ptr<B> pObj;
public:
A {
// do your pre-B stuff
pObj = std::unique_ptr<B>(new B(/* args */));
};
// other stuff
}
I would recommend, make your B class constructor private.. something like
class B
{
private:
B();
}
and no one(from outside the class itself or friend classes) will be able to call default constructor. Also, then you'll have three options for using the class: either to provide a parameterized constructor, or use it as a utility class (one with static functions only), or to create a factory for this type in a friend class.
Can I prevent C++ from calling a default constructor?
You can't if B is not an build in or an aggregate Type.

Is this constructor correct?

I have two classes A and B, and in class A I have an member of type B:
class B {
public:
B(); //default constructor
};
class A {
public:
A(); //constructor
B b;
};
This is the definition of class A's constructor:
A::A() : b()
{}
Here, I tried to initialize b using the initialization list. My question is, is this way to initialize b correct, or am I just creating another temporary object named b inside the constructor of A that has nothing to do with A::b?
This is correct. However, since b is of class type, the default constructor will be called automatically if b isn't mentioned in A::A's initialization list, so you don't need to mention it at all.
This method will initialize the field b with the constructor B::B(). It does not create a temporary local.
Note that in this particular case it's also unnecessary. The default constructor generated for A will do this by itself. There is nothing wrong with being explicit here, it's just unnecessary

How does C++ compiler know the size of a object with superclasses

Exactly as the question states!
Let's say I have the following snippet
class A
{
int x;
int y;
}
class B : public A
{
int z;
}
class C
{
A a;
public C(A a) : a(a){}
}
What would happen if I called C's constructor with a B class, would it copy it's A part data in the class? Or also keep it's B data somewhere?
Thanks in advance! It might be a stupid question but I never understood actually.
If you pass an instance of B to the C constructor that takes an A by value, the B instance will be sliced, and just the A part will remain. So :
would it copy it's A part data in the class?
this.
So, specifically, there is no way to turn the C::a member back into a B instance with the same value for z as the original B instance - that information has been lost during the (irreversible) slicing operation.
What would happen if I called C's constructor with a B class, would it copy it's A part data in the class?
Yes. This is known as slicing - the argument is created using A's copy constructor, which just copies the A subobject.
Or also keep it's B data somewhere?
No.

C++ constructor initialization reference assignment

I am sure this question has been asked before. But I cannot seem to find the exact answer that I am looking for. Basically I am trying to create an object of the class as a member of the other class and pass one of the members by reference to the owned object through the constructor. Sometimes this seems to work, other times I get a random value.
I think that I am not understanding some rudimentary rule of initialization order
Here is the code example:
class Foo
{
public:
Foo();
private:
int b;
Bar c;
};
class Bar
{
public:
Bar(int& parm);
private:
int& b_ref;
};
Foo::Foo(): b(0), c(b)
{}
Bar::Bar(int& parm): b_ref(parm)
{}
What I want is for c to own a reference to b and be able see the value as it changes.
Should I not use the initialization list in this case?
The rule is that objects are initialised in the order of their declaration in the class. In this case, it means that b is initialised before c, so this code should always work correctly.
If you swapped the order of the b and c members, then the int referenced by param would not yet be initialised in the constructor of Bar.

dependent classes as other class members

I have a class B that requires an instance of class A to be constructed:
class B
{
B(A* a); // there is no default constructor
};
Now I want to create a class that contains B as a member, so I also need to add A as a member and provide it to B's constructor:
class C
{
C() : a(), b(&a) {}
A a; // 1. initialized as a()
B b; // 2. initialized as b(&a) - OK
};
But the problem is that if someone occasionally changes the order of the variables definition in the class, it will break
class C
{
C() : a(), b(&a) {}
B b; // 1. initialized as b(&a) while "a" uninitialized
A a; // too late...
};
Is there a good way to resolve this without modifying the classes A and B? Thanks.
Is there a good way to resolve this without modifying the classes A and B?
Turn on compiler warnings; for gcc, this is -Wreorder (which is included in -Wall):
cc1plus: warnings being treated as errors
t.cpp: In constructor 'A::A()':
Line 3: warning: 'A::y' will be initialized after
Line 3: warning: 'int A::x'
Line 2: warning: when initialized here
Alternatively, use a lint-like tool that detects this.
But the problem is that if someone occasionally changes the order of the variables definition in the class…
Why would they do this? I suspect you're worrying too much about what might happen. Even so, you can leave a comment in the class:
A a; // Must be listed before member 'b'!
B b;
Don't underestimate the force of well-placed comments. :) Then allow someone who purposefully ignores them to get what they deserve; you are using C++, after all.
Use the well-known C++ idiom called Base-from-Member to solve this problem.
Define a base class as,
class C_Base
{
A a; //moved `A a` to the base class!
C_Base() : a() {}
};
class C : public C_Base
{
C() : b(&a) {}
B b; // 1. initialized as b(&a) while "a" uninitialized
//A a; // too late...
};
Now, a is guaranteed to be initialized before b.
Store b in a unique_ptr, and set it in the body, not in the initializer list:
class C
{
C() :a() {
b = std::unique_ptr<B>(new B(&a));
}
A a;
std::unique_ptr<B> b;
};
One option would be to not explicitly store the A, but instead to use dynamic allocation to create a new A to store in the B:
class C {
public:
C() : b(new A) {
// handled in initialization list
}
private:
B b;
};
Since this guarantees that the A is created before the B, this should prevent this problem from ever occurring.
The problem is that you are shooting yourself in the foot with the third example. In C++ the order of member variables in a class/struct matters. No matter how you go about solving your particular problem, if you pass uninitialized data to a constructor due to poor class design / member layout, you will be working with unitialized data and possibly get undefined behavior, depending on the sort of code in place.
To address your particular example, if B really requires an A and the relationship is one to one, why not create a new class AB that has both an A object and a B object in the right order and pass the address of A to B. That is:
class AB
{
public:
AB():b_(&a_) {}
private:
A a_;
B b_;
};
now class C can avoid the ordering problem by using AB instead of A and B:
class C
{
public:
...
private:
AB ab_;
};
As forementioned, this of course assumes a 1:1 relationship between A and B. If an A object can be shared by many B objects, things get more complicated.
I'm not sure how much control you have over the implementation and structure of C but is it necessary to use the objects themselves in class C? Could you redefine the class to use pointers instead and then move them from the initialization list, e.g.
class C
{
C()
{
a = new A;
b = new B(a);
}
~C() {delete a; delete b;}
A* a;
B* b;
};
This avoids the issue of order in the declaration, but gives you the new issue of ensuring they're created correctly. Also, if you create A LOT of C's very often, an initialization list is slightly faster.