weak_ptr vs unique_ptr reference - passing interface impl to other object - c++

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.

Related

Alternatives for storing a class member as a raw pointer

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.

C++ How to inherit from interface with a method of other interface type?

I have this structure:
class IA
{
virtual void foo() = 0;
};
class IB
{
virtual IA bar() = 0;
};
So far I made the inherited versions of these two interfaces but I have difficulties with how to make concrete overriding of the IA IB::bar() method?
I mean if I have a class A inheriting from IA then how to declare the A bar() method in a class B which inherits of IB? Or perhaps I should make a third class for this matter?
Thanks! 😊
Edit: Now I have noticed that the bar() definition in IB is as follows:
virtual IA& bar() = 0;
Perhaps this will help?
Welcome to the wonderful world of subtle differences between Java and C++. If you come from a Java background, a value of type IA is of type IA or any subclass thereof. But in C++, a value of type IA is a concrete instance of IA, nothing more and nothing less. And your IA is abstract, hence there are no concrete instances, so as written, your IB::bar is unsatisfiable. There's no well-defined function that can ever have that type.
Instead, when you want to do Java-style inheritance-based polymorphism (which is often not the answer in C++, but we'll assume it's the right tool for the job for the moment), you need a layer of indirection. That can be a raw pointer (IA*), a reference (IA&), or a smart pointer (unique_ptr<IA>).
By convention, we return a smart pointer if the data is intended to be owned by the caller (i.e. the caller is responsible for freeing it when done), a reference if it's owned by someone else, and a raw pointer if we want our fellow programmers to suffer. I'll assume you intend that the caller own the IA value, so we'll use a smart pointer.
Your interfaces can be written
#include <memory>
class IA {
virtual void foo() = 0;
};
class IB {
virtual std::unique_ptr<IA> bar() = 0;
};
And we can concretely implement them as follows
class A : public IA {
virtual void foo() {}
};
class B : public IB {
virtual std::unique_ptr<IA> bar() {
return std::make_unique<A>();
}
};
class IA
{
public:
virtual void foo() = 0;
};
class IB
{
public:
virtual IA& bar() = 0;
};
class A : public IA
{
public:
void foo() override;
};
class B : public IB
{
private:
A m_a;
public:
IA& bar() override;
};
void A::foo() {
...
}
IA& B::bar() {
return m_a;
}

usage of std optional mixed with shared_ptr

I have two derived classes as follows. This is the simplified design version that I have right now.
class A objects are copy-able but they are big. That is why I used reference in the constructor of the derived_1 class.
I used shared_ptr for the class derived_2 in order to make usage of p_a optional. I noticed that I can also use std::optional in the constructor of the class derived_2. By this I can give hint to the user that this argument is indeed optional.
Please take into consideration that this the simplified version and this member p_a is used in all three classes intensively. Also std::shared_ptr<A&> a is not the only argument for the constructor in the real example. I will be thankful if you show me how to use std::optional properly here.
Is it ok to mix std::optional with std::shared_ptr?
class A
{
int m_a;
public:
A(int a) :m_a(a) {};
};
class Base
{
protected:
std::shared_ptr<A> p_a; //Do I need to change type of p_a???
public:
Base() {};
void print()
{
if (p_a)
std::cout << "p_a is allocated\n";
}
void virtual check() = 0;
};
class derived_1 : public Base
{
public:
derived_1(const A& a)
{
p_a = std::make_shared<A>(a);
}
void check() {};
};
class derived_2 : public Base
{
public:
derived_2(std::shared_ptr<A&> a) //can I use std::optional instead??
{
if (a)
p_a = a;
}
void check() {};
};

Is it legal to use the address of a member to initialize a base-class?

Is this code legal?:
class BaseClass
{
public:
BaseClass (int *p) : p_ (p) { }
private:
int *p_;
};
class SubClass : public BaseClass
{
public:
SubClass () : BaseClass (&i_), i_ (123) {}
private:
int i_;
};
It is well-known that the base-class gets constructed before the members of the sub-class, which is why I'm wondering.
Yes, this is fine: while the lifetime of (the relevant instance of) SubClass::i has yet to begin, its storage exists, and a pointer to it may be formed (though not used for much yet).

reference instead of pointer to abstract class

I'm curious if in the following program Base* base in class Container can be replaced with Base& base?
With Base base it can't be replaced, because Base is abstract.
With Base& base the object should be allocated somewhere, so I still would not be able to get rid of the pointer to the allocated object.
#include <iostream>
class Base
{ public:
virtual void str()=0;
};
class A : public Base
{ int i;
public:
A(int i):i(i){}
void str(){std::cout<<i<<std::endl;}
};
class B : public Base
{ double f;
public:
B(double f):f(f){}
void str(){std::cout<<f<<std::endl;}
};
class Container
{ Base *base;
public:
Container(int i) { base=new A(i);}
Container(double f) { base=new B(f);}
void str(){ base->str();}
};
int main ()
{
Container c1(8),c2(13.0);
c1.str();
c2.str();
return 0;
}
With your code, I would't recommend it, because Container is the owner of base and a reference, semantically, means something else (an alias).
Technically, there's nothing stopping you:
class Container
{ Base &base;
public:
Container(int i) : base(*new A(i)) {}
Container(double f) : base(*new B(f)) {}
void str(){ base->str();}
};
Note that references have to be initialized in the initializer list.
You'd still need to clean up the memory, and it would look ugly with a reference:
~Container() { delete &base; }
instead of
~Container() { delete base; }
if you used pointers. Of course, using a std::unique_ptr instead of either of these two would make life a whole lot easier.
Also, be sure to implement (or declare as private or deleted) the copy constructor or assignment operator.
Later spot - You need to provide Base with a virtual destructor, otherwise you'll run into undefined behavior territory when you'll attempt to clean up the memory.