Is this method of passing and storing this to a child object still considered 'acceptable' when using C++17, or is there a more appropriate method, in line with the language and standard?
I am specifically asking regarding passing and storing the Parent object as a plain pointer.
class Child
{
public:
void SetParent(Parent* p)
{
_parent = p;
}
private:
Parent* _parent;
};
class Parent
{
public:
void MyMethod()
{
Child c;
c.SetParent(this);
}
};
Post-C++11 you can use std::weak_ptr<Parent>, assuming you're using std::shared_ptr<Parent> (and inherit from std::enable_shared_from_this in order to generate a std::shared_ptr or std::weak_ptr internally).
Aside from that, yes, it's still acceptable to use a raw pointer to represent lack of ownership or back pointer (until the committee adds some kind of std::owned_ptr<T> / std::ptr_view<T> class).
Related
I am currently trying to learn how to use smart pointers. However while doing some experiments I discovered the following situation for which I could not find a satifying solution:
Imagine you have an object of class A being parent of an object of class B (the child), but both should know each other:
class A;
class B;
class A
{
public:
void addChild(std::shared_ptr<B> child)
{
children->push_back(child);
// How to do pass the pointer correctly?
// child->setParent(this); // wrong
// ^^^^
}
private:
std::list<std::shared_ptr<B>> children;
};
class B
{
public:
setParent(std::shared_ptr<A> parent)
{
this->parent = parent;
};
private:
std::shared_ptr<A> parent;
};
The question is how can an object of class A pass a std::shared_ptr of itself (this) to its child?
There are solutions for Boost shared pointers (Getting a boost::shared_ptr for this), but how to handle this using the std:: smart pointers?
There is std::enable_shared_from_this just for this purpose. You inherit from it and you can call .shared_from_this() from inside the class. Also, you are creating circular dependencies here that can lead to resource leaks. That can be resolved with the use of std::weak_ptr. So your code might look like this (assuming children rely on existence of parent and not the other way around):
class A;
class B;
class A
: public std::enable_shared_from_this<A>
{
public:
void addChild(std::shared_ptr<B> child)
{
children.push_back(child);
// like this
child->setParent(shared_from_this()); // ok
// ^^^^^^^^^^^^^^^^^^
}
private:
// note weak_ptr
std::list<std::weak_ptr<B>> children;
// ^^^^^^^^
};
class B
{
public:
void setParent(std::shared_ptr<A> parent)
{
this->parent = parent;
}
private:
std::shared_ptr<A> parent;
};
Note however, that calling .shared_from_this() requires that this is owned by std::shared_ptr at the point of call. This means that you cannot create such object on stack anymore, and generally cannot call .shared_from_this() from within a constructor or destructor.
You have several problems in you design, that seem to stem from you misunderstanding of smart pointers.
Smart pointers are used to declare ownership. You are breaking this by declaring that both the parents owns all children, but also that each child own it's parent. Both can't be true.
Also, you are returning a weak pointer in getChild(). By doing so, you are declaring that the caller shouldn't care about the ownership. Now this can be very limiting, but also by doing so, you must make sure that the child in question won't get destroyed while any weak pointers are still held, if you would use a smart pointer, it would get sorted out by itself.
And the final thing. Usually, when you are accepting new entities, you should usually accept raw pointers. Smart pointer can have their own meaning for swapping children between parents, but for general usage, you should accept raw pointers.
I use a library that only returns references to created objects Entity& Create(int id). In my class, I need to create one of these and store it.
I had thought to use class member std::reference_wrapper<Entity> MyClass::m_Entity but the problem is, I would like to create this object in a call to a method like MyClass::InitEntity() – so I run into a compile error "no default constructor available" because m_Entity is not initialised in my constructor.
Is there any way around this, other than to change my class design? Or is this a case where using pointers would make more sense?
Is MyClass in a valid state if it doesn't have a valid reference to an Entity? If it is, then you should use a pointer. The constructor initializes the pointer to nullptr, and the InitEntity function assigns it to the address of a valid Entity object.
class MyClass
{
public:
MyClass(): _entity(nullptr) {}
void InitEntity() { _entity = &Create(123); }
void doSomethingWithEntity()
{
if (_entity) ...
}
private:
Entity *_entity;
};
If MyClass isn't in a valid state without a valid reference to an Entity, then you can use a std::reference_wrapper<Entity> and initialize it in the constructor.
class MyClass
{
public:
MyClass(): _entity(Create(123)) {}
void doSomethingWithEntity()
{
...
}
private:
std::reference_wrapper<Entity> _entity;
};
Of course which one you go with depends on how MyClass is supposed to be used. Personally, the interface for std::reference_wrapper is a little awkward for me, so I'd use a pointer in the second case as well (while still ensuring that it's always not null).
I am currently trying to learn how to use smart pointers. However while doing some experiments I discovered the following situation for which I could not find a satifying solution:
Imagine you have an object of class A being parent of an object of class B (the child), but both should know each other:
class A;
class B;
class A
{
public:
void addChild(std::shared_ptr<B> child)
{
children->push_back(child);
// How to do pass the pointer correctly?
// child->setParent(this); // wrong
// ^^^^
}
private:
std::list<std::shared_ptr<B>> children;
};
class B
{
public:
setParent(std::shared_ptr<A> parent)
{
this->parent = parent;
};
private:
std::shared_ptr<A> parent;
};
The question is how can an object of class A pass a std::shared_ptr of itself (this) to its child?
There are solutions for Boost shared pointers (Getting a boost::shared_ptr for this), but how to handle this using the std:: smart pointers?
There is std::enable_shared_from_this just for this purpose. You inherit from it and you can call .shared_from_this() from inside the class. Also, you are creating circular dependencies here that can lead to resource leaks. That can be resolved with the use of std::weak_ptr. So your code might look like this (assuming children rely on existence of parent and not the other way around):
class A;
class B;
class A
: public std::enable_shared_from_this<A>
{
public:
void addChild(std::shared_ptr<B> child)
{
children.push_back(child);
// like this
child->setParent(shared_from_this()); // ok
// ^^^^^^^^^^^^^^^^^^
}
private:
// note weak_ptr
std::list<std::weak_ptr<B>> children;
// ^^^^^^^^
};
class B
{
public:
void setParent(std::shared_ptr<A> parent)
{
this->parent = parent;
}
private:
std::shared_ptr<A> parent;
};
Note however, that calling .shared_from_this() requires that this is owned by std::shared_ptr at the point of call. This means that you cannot create such object on stack anymore, and generally cannot call .shared_from_this() from within a constructor or destructor.
You have several problems in you design, that seem to stem from you misunderstanding of smart pointers.
Smart pointers are used to declare ownership. You are breaking this by declaring that both the parents owns all children, but also that each child own it's parent. Both can't be true.
Also, you are returning a weak pointer in getChild(). By doing so, you are declaring that the caller shouldn't care about the ownership. Now this can be very limiting, but also by doing so, you must make sure that the child in question won't get destroyed while any weak pointers are still held, if you would use a smart pointer, it would get sorted out by itself.
And the final thing. Usually, when you are accepting new entities, you should usually accept raw pointers. Smart pointer can have their own meaning for swapping children between parents, but for general usage, you should accept raw pointers.
I have a class and the constructor accepts a parameter. For example,
class Entity
{
private:
int number_;
public:
Entity(int number):number_(number)
{
std::cout << "Entity object created";
}
}
// header
class SuperEntity
{
private:
Entity *entity_;
public:
SuperEntity(int value);
};
// source
SuperEntity::SuperEntity(int value)
{
entity_ = new Entity(value);
}
class SuperEntity has a private member Entity. Since in order to instantiate Entity you need to pass in an int to it's constructor and cannot be done the declaration file (superentity.h) because the int value needed to instantiate Entity is not available yet, is it okay to dynamically allocate Entity in SuperEntity's constructor? Is this a bad practice? Thanks.
As Dietmar remarked, use a member initializer list:
class SuperEntity
{
Entity entity_;
public:
SuperEntity( int value )
: entity_{ value }
{}
};
It is ok per the language but not necessarily the best pratice.
Use an object if you can.
Failing that, use a smart pointer instead of a raw pointer. See std::shared_ptr and std::unique_ptr.
If you must use a raw pointer, make sure to follow The Rule of Three.
It's fine to have a pointer data field for a decoupled has-a relationship. If you really want to stick to pointers then you should prefer the std::unique_ptr to raw pointer and utilize the std::make_unique function in the member initializer list of the constructor:
class SuperEntity {
private:
std::unique_ptr<Entity> entity_;
public:
SuperEntity(int value);
};
SuperEntity::SuperEntity(int value)
: entity_(std::make_unique<Entity>(value))
{}
If you want to have a classic has-a relationship abstraction, namely a data field whose lifetime is bound to owners lifetime then loose the pointer and go with the object:
class SuperEntity {
private:
Entity entity_;
public:
SuperEntity(int value);
};
SuperEntity::SuperEntity(int value)
: entity_(value)
{}
};
The entity_ object will be destroyed once the object of type SuperEntity goes out of scope.
I am currently trying to learn how to use smart pointers. However while doing some experiments I discovered the following situation for which I could not find a satifying solution:
Imagine you have an object of class A being parent of an object of class B (the child), but both should know each other:
class A;
class B;
class A
{
public:
void addChild(std::shared_ptr<B> child)
{
children->push_back(child);
// How to do pass the pointer correctly?
// child->setParent(this); // wrong
// ^^^^
}
private:
std::list<std::shared_ptr<B>> children;
};
class B
{
public:
setParent(std::shared_ptr<A> parent)
{
this->parent = parent;
};
private:
std::shared_ptr<A> parent;
};
The question is how can an object of class A pass a std::shared_ptr of itself (this) to its child?
There are solutions for Boost shared pointers (Getting a boost::shared_ptr for this), but how to handle this using the std:: smart pointers?
There is std::enable_shared_from_this just for this purpose. You inherit from it and you can call .shared_from_this() from inside the class. Also, you are creating circular dependencies here that can lead to resource leaks. That can be resolved with the use of std::weak_ptr. So your code might look like this (assuming children rely on existence of parent and not the other way around):
class A;
class B;
class A
: public std::enable_shared_from_this<A>
{
public:
void addChild(std::shared_ptr<B> child)
{
children.push_back(child);
// like this
child->setParent(shared_from_this()); // ok
// ^^^^^^^^^^^^^^^^^^
}
private:
// note weak_ptr
std::list<std::weak_ptr<B>> children;
// ^^^^^^^^
};
class B
{
public:
void setParent(std::shared_ptr<A> parent)
{
this->parent = parent;
}
private:
std::shared_ptr<A> parent;
};
Note however, that calling .shared_from_this() requires that this is owned by std::shared_ptr at the point of call. This means that you cannot create such object on stack anymore, and generally cannot call .shared_from_this() from within a constructor or destructor.
You have several problems in you design, that seem to stem from you misunderstanding of smart pointers.
Smart pointers are used to declare ownership. You are breaking this by declaring that both the parents owns all children, but also that each child own it's parent. Both can't be true.
Also, you are returning a weak pointer in getChild(). By doing so, you are declaring that the caller shouldn't care about the ownership. Now this can be very limiting, but also by doing so, you must make sure that the child in question won't get destroyed while any weak pointers are still held, if you would use a smart pointer, it would get sorted out by itself.
And the final thing. Usually, when you are accepting new entities, you should usually accept raw pointers. Smart pointer can have their own meaning for swapping children between parents, but for general usage, you should accept raw pointers.