I have a C++ class hierarchy that looks something like this:
class A;
class B
{
public:
B( A& a ) : a_( a )
{
};
private:
A& a_;
};
class MyObject
{
public:
MyObject() : b_( a_ )
{
};
private:
A a_;
B b_;
};
Occasionally, it will happen that in B's destructor I will get invalid access exceptions relating to its reference of A. It appears that A is destroyed before B.
Is there something inherently wrong with using class members to initialize other members? Is there no guarantee of the order of destruction?
Thanks,
PaulH
EDIT: I missed the MyObject part of your question so my original answer will probably not be of much help. I guess your problem lies in the code you did not post, the stripped-down example should work fine.
Class B does not “own” the object passed by reference, therefore the objects a and b have different life cycles. If the object refered to by B::a_ is destroyed, B's destructor will access an invalid reference.
Some code to explain what I mean:
class A;
class B {
public:
B(A a) : a_(a) {} // a is copied to a_
~B() { /* Access a_ */ }
private:
A a_;
};
class C {
public:
C(A& a) : a_(a) {} // a_ is a reference (implicit pointer)
// of an external object.
~C() { /* Access a_ */ }
private:
A& a_;
};
int main(int argc, char** argv) {
A* a = new A();
B b(*a);
C c(*a);
delete a;
// Now b has a valid copy of a, c has an invalid reference.
}
In the code above the destruction order is well defined.
The destruction order is the reverse of the creation order.
The creation order is the order the members were declared within the class.
So in this case:
Default Construction:
a_: constructed first using default constructor.
b_: constructed using a valid a_ passed to the constructor.
Destruction:
b_: destroyed first. The destructor can use the reference to a
As long as the object has not been copied (see below)
a_: destroyed second.
But you have a potential problem if you make a copy of the object using the copy constructor.
The following copy constructor is defined by the compiler:
MyObject::MyObject(MyObject const& copy)
:a_(copy.a_)
,b_(copy.b_)
{}
So you may have a potential problem here. As the copy will contain an object 'b_' that contains a reference that was copied from another object. If the other object is destroyed then this 'b_' will have an invalid reference.
a_ should be constructed before b_, and b_ should be destructed before a_, based on the order you have defined them in MyObject.
Related
I was looking to create a function that is capable of working with any Derived Object of A.
However in this example, I can't seem to be able to use B Object in a function that has a A Typing on it. Is there any way I pass B into the Function?
class A {
public:
A() {
}
};
class B :A {
public:
B() {
}
};
void function(A a) {
return;
}
int main(void) {
B b();
function(b);
}
I've commented on the fixes needed inline:
class A {
public:
A() {}
};
class B : public A { // public inheritance or A will be an inaccessible base of B
public:
B() {}
};
void function(const A& a) { // take a reference to an A to avoid copy-slicing
// ... work with the A part of the object you reference with `a`
}
int main() { // void not needed (but not an error as such)
B b; // not a function declaration anymore
function(b);
}
Actually you are lucky. You made two mistakes that caused passing b to the function fail, while in fact without that other mistakes you can pass b to the function but it would do the wrong thing silently.
First the two mistakes: B b(); declares a function. To declare a default constructed B you write B b;. Then B inherits privately, hence you cannot convert a B to an A. Thats what the error your code causes have told you.
However, after fixing those (and removing user declared constructors taht shouldnt be there when they do nothing)...
class A {};
class B : public A {};
void function(A a) {}
int main(void) {
B b;
function(b); // object slicing !!
}
This code compiles without errors, but usually it does the wrong thing!
Any B can be converted to an A because the inheritance is public, but what happens is object slicing: What is object slicing?. If B had any members not in A then they would all be lost when passing it to function. Its not an issue here because neither A nor B have any members, but in general you want to avoid objects to get sliced.
TL;DR: References / pointers are needed for polymorphism. Pass by (const) reference:
void function(const A& a) {} // does not modify a
void function(A& a) {} // modifies a
If an object A contains a member Object B and Object B has a pointer to its parent Object A, do I need to specify a copy constructor for Object B?
Assume there is no dynamic allocation.
Furthermore, does the rule of 3 apply here?
Your design implements composition with a bidirectional navigation. This can be perfectly valid.
However, as Sergey pointed out in the comments, such a design is not without problems.
Suppose you have a class Object and a class Container that contains an Object. Here some fundamental questions:
Container c;
Object mo1; // Q1: should this be valid ? (i.e. is an object without parent allowed
Object mo2 = c.o; // Q2: is this acceptable ? Q3: Who is parent of mo2 ?
Look at questions Q2 and Q3: if such initialization is acceptable, then the immediate question is what parent do you want:
if mo2 should have no parent, you need a copy constructor according to rule of 3, to clear the parent.
if mo2 should refer to the same parent (although it is not a member), you could keep the default copy constructor.
Example:
struct Container;
struct Object{
Container *parent;
Object (Container *p=nullptr) : parent(p) { cout << "construct object"<<endl; }
// copy constructor or compiler generated one depending on Q2+Q3
~Object() {}
...
};
struct Container {
Object o;
Container() : o(this) {}
};
If such initialization is NOT acceptable, you should forbid copy constuction explicitely in the code.
Object (const Object &o) = delete;
Important note: the Container might also need a copy constructor. Whatever you decide for Object, you might have to deal with in the Container if it has to be copiable. In no way could you use there the copy constructor of the Object.
Important note 2: the Container might itself be used in more complex situations. Take the example of a vector<Container>. When new containers are added to the vector, relocation might be performed, which could invalidate the pointers if you didn't provide a Container copy constructor that takes care !
Yes, you should think about implementing a copy constructor even if the child object doesn't manage any handles or pointers to resources (which will be blindly shared with a copy without such a constructor leading to familiar problems which you clearly understand).
The reason for this is that the copy of a parent's child is not necessarily considered that parent's child!
If the copy of a child is considered to be under the same parent, and the parent has a list of children, whose responsibility is it to insert into that list? It has to be done somewhere. If it's not done in the copy constructor, then every place in the code where a copy is performed has to remember to take some additional action, like copy.register_with_parent(). Any place where this is forgotten is quite probably a bug.
If the copy of a child is not considered to be under the same parent, a naive member-for-member copy will have a parent pointer anyway. What is an object doing with a pointer to a parent which is not actually its parent? Something has to clear that pointer, or set it to some "default parent of all orphaned child objects" or whatever. This would be done in the copy constructor.
If you don't know the answers to these questions, then define a copy constructor which is not implemented, so that copying doesn't take place until you do know what the behavior should be. So, copy constructor again, albeit declared but not defined!
Oh, but parent object can't have it's derieved class object unless allocated with pointer:
class B;
class A
{
B son;
};
class B : public A
{
A* parent;
};
Results:
field 'son' has incomplete type 'B'
B son;
^
Then, if you did it with allocation:
class B;
class A
{
public:
A(void);
~A();
private:
B* son;
};
class B : public A
{
public:
B() {} // <-depends what you want to archieve
private:
A* parent;
};
A::A(void) : son(new B) {}
A::~A() {delete son;}
Questions arise at this point. Do you want the parent pointer to point to global object A, allocate it's own A object, or to point to this ?
Global variable - no need to write copy constructor:
// A class not changed
A global;
class B : public A
{
public:
B() {parent = &global;}
...
If object allocates it's own A object, then you need to stick to rule of 3 (or 4). What more, your class A needs to stick to this rule too, since we will copy parent object, so every B object has it's own:
class B;
class A
{
public:
A(void);
A(const A& other);
A& operator=(const A& other);
~A();
private:
B* son;
};
class B : public A
{
public:
B() : parent(new A) {}
B(const B& other) : parent(new A(*other.parent)) {}
B& operator=(const B& other);
~B() {delete parent;}
private:
A* parent;
};
A::A(void) : son(new B) {}
A::A(const A& other) : son(new B(*other.son)) {}
A& A::operator=(const A& other)
{
delete son;
son = new B(*other.son);
return *this;
}
B& B::operator=(const B& other)
{
delete parent;
parent = new A(*other.parent);
return *this;
}
A::~A() {delete son;}
And finally, object B points to the same object base class, no need for anything special like with global variable:
class B;
class A
{
public:
A(void);
~A();
private:
B* son;
};
class B : public A
{
public:
B() : parent(this) {} // pointless, but yeah, nothing else is needed.
private:
A* parent;
};
A::A(void) : son(new B) {}
A::~A() {delete son;}
It's not about what should be done and what shouldn't. It all depends what you want to achieve.
I have a few classes, each depends on using an instance of another object in a dependency chain. It looks something like this:
class A {} ;
class B
{
A& m_a;
public:
B(A& a) : m_a(a) { }
};
class C
{
B& m_b;
public:
C(B& b) : m_b(b) { }
};
To protect myself from holding dangling references because of order of destruction, I'm holding all of these in a container class, like this:
struct Data
{
Data() : m_b(m_a), m_c(m_b) { }
A m_a;
B m_b;
C m_c;
};
Assuming the order of the members in class Data matches the dependency order, is it safe to do so? Are there any pitfalls in holding these references which I'm missing?
It's OK. Class-members are initialized in order, in which they are declared, so:
m_a initialized -> m_b initialized with reference to m_a -> m_c initialized with reference to m_b.
No problem - C++ guarantees to construct the class members in the order they are declared, so you are safe.
Consider this code:
struct A {};
struct B
{
B(A* a) : a(a) {}
private:
A* a;
};
struct base
{
base(B b) : b(b) {}
protected:
A a;
private:
B b;
};
struct derived : public base
{
derived() : base(B(&a)) // <-- IS THIS OK?
{}
};
Here, the base class needs a B object passed to its constructor by the derived class, and the B object refers to an A object, but the A object lives inside the base class.
The constructor of the B object does not do anything to the A pointer except store it, so I'm thinking this should be OK, but it still feels wrong because technically the A object doesn't yet exist at the time I'm passing it to the base constructor.
Can I get into trouble doing this or should this be OK?
It is safe as long as you don't use a in B's constructor, as the object a isn't constructed yet.
I mean, you shouldn't dereference the pointer a in B's constructor; however after base's constructor is executed, you can safely dereference B::a in other methods of B.
a has not been constructed, but it does have an address, and it is legal to take its address at this point. You do need to be sure, though, not to use the pointer in the constructor of B (i.e., don't dereference the pointer).
This is all rather awkward, though.
I have the code:
class A{ //base class
public:
virtual std::string getString(){return "class A";}
};
class B: public A{
public:
std::string getString() {return "it is B class";}
};
class C{
public:
C(){
B b;
a = b;
}
std::string test() {return a.getString();}
private:
A a;
};
int main()
{
C c;
std::cout << c.test();
return 0;
}
c.test() says "class A", but how I can call method getString() from class B and not A?
Thanks!
The problem is, your B object gets sliced when assigned to an A object. This is because you assigned by value, not by reference or pointer. Since you declared a like this
A a;
what happens during the assignment a = b is that the actual state of b is copied over into a. However, since a is a value object, only the A part of object b is copied, and its "B-ness" is completely lost!
To avoid this, you need to declare a as a pointer type, as suggested by others (a reference would also work, but then you would need to considerably rewrite your example, since you can't assign to references, only initialize them). If a is a pointer (A*), the assignment a = b makes a point to the object represented by b, which is still a B object, thus you will observe the polymorphic behaviour you expected. However, in this case, you must ensure that b stays alive even after exiting the constructor - otherwise you leave a dangling reference which causes undefined behaviour (read: bad things you don't want to happen) when dereferenced.
Since a pointer example was already shown by #Nawaz, I will give another using a reference:
class C{
public:
C() : a(b) { // references must be initialized in the constructor initializer list
}
std::string test() {return a.getString();}
private:
B b; // moved to class scope to ensure that it stays alive
A& a;
};
You need to implement like this:
class C{
public:
C(){
a = new B;
}
std::string test() {return a->getString();}
private:
A *a;
};
This will call getString() from class B and not A.
What you're trying to do is called "dynamic polymorphism" which is achieved through pointer (or reference) of type base class (which is A), but the pointer points to an object of type derived class (which is B).
Because your member a is not an A*, it is an A instance. Therefore you are just assigning the A part of B to variable a. if you convert a to an A*, you will get the expected result.
You are slicing therefore it will not work. a is an A it is not a B.
To work your class member variable a must be a pointer or a reference.
As a pointer
class C{
public:
C(){
a = new B;
}
std::string test() {return a->getString();}
private:
A *a;
};
As a reference
class C{
public:
C() : a( *(new B) )
{
}
std::string test() {return a.getString();}
private:
A &a;
};
Of course the code I have produced leaks but will work with the virtual function.