In the code example shown below - in Container class, it owns (and is responsible fore destroying) two objects c, d, which are subclasses of an abstract class B. Container object can create new ObjectDisplay that takes a kind of B in its constructor. I can pass the abstract type B as a pointer into ObjectDisplay and store it as a RAW pointer. But it's not ideal to store & use a raw pointer and always check if it's a null pointer. If B wasn't an abstract class, I could pass it in ObjectDisplay as a reference (ie. ObjectDisplay (B& b)). But since I can't change B, I wonder what's the aternative of storing B* object as a raw pointer in ObjectDisplay?
// B is abstract
class B
{
public:
virtual int getDefault() = 0;
};
class C : public B
{
public:
int getDefault() override { return 1; }
};
class D : public B
{
public:
int getDefault() override { return 5; }
};
class ObjectDisplay
{
public:
ObjectDisplay (B* b) : object (b) {}
void someFunction()
{
const auto result = b->getDefault();
// do something
}
private:
B* object;
};
class Container
{
public:
void addDisplay()
{
displays.push_back (ObjectDisplay (&c));
displays.push_back (ObjectDisplay (&d));
}
private:
C c;
D d;
std::vector<ObjectDisplay> displays;
};
If B wasn't an abstract class, I could pass it in ObjectDisplay as a reference
No, if B is an abstract class, you can still pass it by reference. B& object can be bound to an instance of B's subclass. It behaves almost the same as pointers.
As quoted in cppref:
That is to say, if a derived class is handled using pointer or reference to the base class, a call to an overridden virtual function would invoke the behavior defined in the derived class.
Declare a member of B& in ObjectDisplay and construct it through a reference.
class ObjectDisplay
{
public:
ObjectDisplay (B& b) : object (b) {}
private:
B& object;
};
class Container
{
public:
void addDisplay()
{
displays.push_back (ObjectDisplay (c));
displays.push_back (ObjectDisplay (d));
}
};
See online demo
Aside:
Since you are passing a temporary ObjectDisplay object directly constructed in push_back, I recommend you to use emplace_back.
void addDisplay()
{
displays.emplace_back (c);
displays.emplace_back (d);
}
If B wasn't an abstract class, I could pass it in ObjectDisplay as a reference (ie. ObjectDisplay (B& b)). But since I can't change B, I wonder what's the aternative of storing B* object as a raw pointer in ObjectDisplay?
Just because B is an abstract class does not mean you are required to pass it around and store it as a pointer. You CAN pass it around and store it as a reference as well. Polymorphism works with pointers AND references. And using a reference would indeed solve your nullptr issue, eg:
class ObjectDisplay
{
public:
ObjectDisplay (B& b) : object (b) {}
void someFunction()
{
const auto result = object.getDefault();
// do something
}
private:
B& object;
};
class Container
{
public:
void addDisplay()
{
displays.push_back (ObjectDisplay (c));
displays.push_back (ObjectDisplay (d));
}
private:
C c;
D d;
std::vector<ObjectDisplay> displays;
};
Online Demo
As long as c and d outlive the ObjectDisplay objects in displays, you will be just fine, whether you use pointers or references.
Related
I have a base class that holds a vector of pointers to derived classes and a virtual recursive method like this:
class Base
{
std::vector<Base*> vec;
public:
Base(std::vector<Base*> vec = {}) : vec {vec} {}
virtual ~Base() = default;
virtual void f() const
{
if (vec.size() == 0)
throw std::logic_error("Last children should implement f()")
for (auto * part : vec)
part->f();
}
}
The method f() is recursive and the last child of Base should override it. For example the following works
Where the first derived class is
class B : public Base
{
B() : Base() {}
virtual void f() const
{
std::cout << "In B\n";
}
}
class A : public Base
{
B b_,c_;
public:
A(B b, B c) : Base({&b_,&c_}), b_ {b}, c_{c} {}
}
If I call
B b,c;
A a(b,c);
a.f()
It will print twice "In B"correctly. Notice that A does not override f() so Base::f() is called, this loops and calls B::f() twice, once for b and once for c. However if I go one nested class more, for example by having
class C : public Base
{
A a_;
B b_;
public:
C(A a, B b): Base({&a_, &b_}), a_{a}, b_{b} {}
}
And I call C::f() the program segfaults. I suppose this is because there are some temporaries and their destructors delete the pointers held by Base. Is the solution to this to hold shared_ptr? or is there a better design? I cannot hold unique_ptr cause that would make all derived classes not copyable.
public C(A a, B b): Base({&a, &b}), a{a}, b{b} {}
Firstly, you can't have public there.
Your base points to the parameters of the constructor. Those parameters are destroyed when the constructor finishes and the pointers become invalid. Point to the members instead:
C(A a, B b): Base({&this->a, &this->b}), a{a}, b{b} {}
Note however that the implicit copy and move constructors and assignment operators of the class are broken because they will make the copied object point to the members of the copy source, rather than the members of the copy itself. So, you'll need implement those as well.
is there a better design?
Possibly a better approach: Instead of storing the pointers in the base sub object, write a virtual function that derived classes can override, and which returns a range of pointers to children.
i am new to shared pointers. I would like to have a method which returns reference to an object. The object will be created inside a method by make_shared and also stored in a vector as a shared pointer of base class. I guess my code is not the best way to do it. Any suggestions?
Thank you.
class A {
public:
};
class B : public A {
public:
};
class Factory {
public:
B& createA() {
std::shared_ptr<B> b = std::make_shared<B>();
container.push_back(b);
return *b;
}
private:
std::vector<std::shared_ptr<A>> container;
};
int main() {
Factory factory;
B& b = factory.createA();
// work with B here...
}
Here is an example code:
class Interface
{
public:
virtual ~Interface(){}
virtual void fun() = 0;
};
class InterfaceImpl: public Interface
{
public:
void fun() override
{}
};
class B
{
public:
B(const std::shared_ptr<Interface>& impl /*std::unique_ptr<Interface>& impl* ?*/): impl_(impl){}
private:
std::weak_ptr<Interface> impl_;
//std::unique_ptr<Interface>& impl_; ?
};
class A
{
public:
A(): impl_(std::make_shared<InterfaceImpl>()), b(impl_){}
private:
std::shared_ptr<Interface> impl_;
//std::unique_ptr<Interface> impl_ ?
B b;
};
Class A contains an interface implementation and other object of type B. That object also need to use an interface implementation. I wonder which types of smart pointers should be used to create interface impl in class A and pass that impl to class B. Should I use shared_ptr in class A and weak_ptr in class B or unique_ptr in class A and a reference to unique ptr in class B ?
I believe the default choice should be that A, that owns the Interface, should hold it by unique_ptr. Then B, which does not own the Interface, should hold it by normal reference or raw pointer.
A reference to a unique_ptr rarely makes sense. It offers no additional safety guarantees over a raw pointer or a normal reference but adds confusion over ownership.
class Interface
{
public:
virtual ~Interface(){}
virtual void fun() = 0;
};
class InterfaceImpl: public Interface
{
public:
void fun() override
{}
};
class B
{
public:
B(const Interface& impl): impl_(impl){}
private:
const Interface& impl_;
};
class A
{
public:
A(): impl_(std::make_unique<InterfaceImpl>()), b(*impl_){}
private:
std::unique_ptr<Interface> impl_;
B b;
};
This is assuming the lifetime of B is shorter than the lifetime of A so that B can guarantee that the Interface is alive. If you can't make that guarantee then you can start thinking about shared_ptr and weak_ptr pair but I don't think that should be your first choice. It looks like in your case you can make that guarantee.
As for whether B should hold a normal reference or a raw pointer that comes down to whether impl_ can be null (which doesn't seem to be the case here). Also, holding a reference restricts what you can do with B. It makes it unassignable and you can't reseat the reference to point to a different impl.
I have a class A that provides methods to construct instances of class B. And B holds a private reference to A and provides a constructor to set this reference.
class A {
public:
B* construct_B ();
}
class B {
private:
const A& private_A;
public:
B ( const A& my_A ): private_A (my_A) { }
}
The implementation of construct_B takes care of dynamic allocation and passing the reference to itself via this.
How do I implement this setup in such a way that I make sure that the lifetime of A is longer than B so that its reference remains valid? Notice that I don't care about all the possibilities of construct_B instead of returning a raw pointer I could return a smart pointer or similar.
One possible way of solving this could be having B instead of holding a reference to hold a smart pointer to A, and instead of dynamically allocating B in construct_B to take a static reference to B and then set it's pointer, something like
class A :
public std::enable_shared_from_this<A> {
public:
void setup_B ( const B& my_B ) {
my_B.set_A (shared_ptr_from_this() ) ;
}
class B {
private:
const shared_ptr<A> private_A_ptr;
public:
void set_A ( const shared_ptr<A> my_A ):
private_A_ptr (my_A) { }
}
which then could be implemented by
int main () {
A static_A;
B static_B;
A.setup_B (static_B);
}
Does the shared_ptr of this last construction avoid the problem of A being deleted before B?
shared_ptr is your answer. Something like this:
#include <memory>
struct A;
class B {
const std::shared_ptr<A> private_A_ptr;
public:
B(std::shared_ptr<A> parent) : private_A_ptr(std::move(parent)) {}
};
struct A :
std::enable_shared_from_this<A>
{
B make_b() {
return B(shared_from_this());
}
};
int main()
{
// this would be invalid:
//A a;
//auto b = a.make_b();
// but this is fine
auto pa = std::make_shared<A>();
auto b = pa->make_b();
// later...
pa.reset();
// A still exists because ownership was shared with b
}
I would like to ask the following:
Suppose that we have three classes in C++: A, B and C. An object of class A is creating and owning an object of class B. It is then giving the reference of B to an object of class C to be stored as a pointer variable. So, what is the best practice to inform C, that the pointer to B is no longer valid (and should be set to null), if A is deleted?
Is there a general approach or for example a Qt specific one?
Use std::weak_ptr
Example (live demo here)
class A
{
private:
std::shared_ptr<B> myB;
public:
A() :
myB(std::make_shared<B>())
{
}
std::weak_ptr<B> GetB()
{
return myB;
}
};
class B
{
};
class C
{
private:
std::weak_ptr<B> theB;
public:
C(std::weak_ptr<B> b) :
theB(b)
{
}
void test()
{
auto b = theB.lock();
if (b)
{
// theB is still valid
}
else
{
// theB is gone
}
}
};