I have a parent object that has some other objects as fields. The parent object fully owns these fields: they can be declared as fields of the parent object (MyPart part), directly, not as references.
I would prefer to initialize these fields in constructor, but in some cases they do need to reach the parent object instance and call methods on it as they work. With raw pointers, I could simply pass this to they constructors or setters. I have just discovered however that creating smart pointer from this is something near impossible, because such pointer would not know when the parent object goes out of scope (for sure not where the constructor returns!). But I do known that being the fields of the parent object, these children definitely cannot outlive it.
The only "clean" idea I can so far imagine is to construct the object in some factory method where reference to the constructed parent can be a smart pointner, then construct children separately and use the setter to compose the parent. Is this really the best approach?
As we have now policy to avoid raw pointers at all costs, what other options could I have? Or just using raw pointers in such a case is appropriate?
4 Answers 4
You can use a reference to the parent. As a sketch
class Parent;
class Child {
private:
Parent & parent;
public:
Child(Parent & p) : parent(p) {}
// other members
};
class Parent {
private:
Child child;
public:
Parent() : child(*this) {}
// other members
};
As a bonus, the compiler will refuse to let you construct a Child without a Parent. You can go further, and make Child only privately constructable, and friend Parent.
-
2The problem with using a reference is that
Childis not assignable anymore. Using a pointer member variable avoids that.D Drmmr– D Drmmr2019年03月23日 20:02:11 +00:00Commented Mar 23, 2019 at 20:02 -
@DDrmmr? it's a subobject of
Parent, it doesn't need to be assignableCaleth– Caleth2019年03月23日 20:44:28 +00:00Commented Mar 23, 2019 at 20:44 -
Bracket missing at the start of
Parent. =)Elliott– Elliott2021年02月02日 04:00:08 +00:00Commented Feb 2, 2021 at 4:00 -
Reference as member is only valid during the expression which created it. It is a cause of undefined behavior in the next valid expression. It is not because the referenced object goes out of scope, it is just the rule of the language. This needs to be handled using pointers or copy semantics instead. There is an entire paragraph describing the UB caused by keeping member reference in 14882:2003 that I'm too lazy to search for.nurettin– nurettin2021年11月23日 07:38:02 +00:00Commented Nov 23, 2021 at 7:38
-
@nurettin
childis created with a valid reference to theParentobject, and is destroyed strictly before theParent. Did you miss the part wherechildis a data member of theParentobject?Caleth– Caleth2021年11月23日 09:13:05 +00:00Commented Nov 23, 2021 at 9:13
Just use a raw pointer, it is a non-owning pointer that has no implication on life-cycle.
And the child holding a parent pointer will not outlive the parent as you say they are non-pointer/reference members so parent will clean them up.
Try to avoid coupling the field classes to the parent class. Any data that is in the parent, but is needed by the field, can be passed when calling a function of the field. Any function of the parent that needs to be called by a function of the field can be passed as a callback function (e.g. using std::function).
A mutual dependency between classes is often an indication that the code is ill structured. The flow of the program can quickly become unwieldy, making the code hard to maintain.
If the field really needs a reference to the parent, you can safely pass the this pointer to the field's constructor (even in the parent's initializer list), provided the field's constructor doesn't access the passed pointer, but only stores it.
I think you can use mix of non-const reference and non-static data member initialization.
#include <iostream>
using namespace std;
class Parent;
class Child {
private:
Parent& parent;
public:
Child(Parent& parent): parent(parent) { cout << "Child ctor" << endl; };
~Child() { cout << "Child dtor" << endl; } ;
};
class Parent {
private:
Child child { Child(*this) };
public:
Parent (){ cout << "Parent ctor" << endl; };
~Parent() { cout << "Parent dtor" << endl; } ;
};
int main(int argc, char *argv[]) {
Parent parent;
}
output:
~$ g++ -o test test.cpp -std=c++11 && ./test
Child ctor
Parent ctor
Parent dtor
Child dtor
Parent&, and pass it*thisif you truly need access to the parent. If the child needs access to the parent through astd::shared_ptr<>, you can have the parent inheritstd::enable_shared_from_thissee here. This will allow ashared_ptrto be constructed bythis->shared_from_this(). Unless you are implementing a data structure this is almost certainly poorly designed. Whateverchildneeds ofParenttry and extract it into a common class, this way it can be fully constructed before use.