The following is some architecture I have designed. I have a class X that has a variable member Y that is a pointer (or reference, I haven't decided yet) to class A. Class A is an abstract class. The user of X can create his own derived class from A (for example B:A) and pass it to X (probably, in the constructor) which, somehow, will store it in Y.
To make this work, the user should dynamically allocate an object of type B:A and pass it to X. However, can you think of a simpler way, for the user, of doing this without having to call new? Ideally, I would like the user to simply create an object of type B:A and pass it. Then, the constructor of X, somehow, would define Y using it (maybe creating a copy of B:A). The problem is that X doesn't know which derived type is passed and what size it is.
I want to create a single constructor to which the user could pass any type derived from A as parameter, and would be converted into a member variable. It should allow the user to create his own type for taking advantage of polymorphism.
class A {...}; // Abstract class (has pure virtual members)
class B : public A {...}; // Class defined by the user
class X
{
public:
X(A ¶m) { /* abc is defined */ }
A* abc;
}
Some ideas I had: Could it work a pure virtual copy assignment operator overloading at A? And having a member at B:A that specifies the size of B:A? However I still don't know how to make it work.
How to solve it? Is there maybe a better way? Thanks in advance.
CodePudding user response:
On the assumption that class X
is going to own param
after it is passed in, you can do this:
#include <iostream>
#include <memory>
class A { public: virtual ~A () {} };
class B : public A { public: ~B () { std::cout << "B destroyed"; } };
class X
{
public:
X (std::unique_ptr <A> ¶m) : m_param (std::move (param)) { }
private:
std::unique_ptr <A> m_param;
};
int main ()
{
std::unique_ptr <A> b = std::make_unique <B> ();
X x (b);
}
When run, this code prints B destroyed
. Note that, for this to work, A
must have a virtual destructor.
If ownership of param
is to be shared with the caller and / or other objects, use std::shared_ptr
instead (but this has more overhead). Or, as @Remy says, if you can guarantee that the lifetime of param
exceeds that of x
, you can store a raw pointer (or reference).
CodePudding user response:
To make this work, the user should dynamically allocate an object of type
B:A
and pass it toX
. However, can you think of a simpler way, for the user, of doing this without having to callnew
?
Polymorphism is not dependent on new
being used. It simply requires a pointer/reference to the polymorphic object. The caller could create its derived object statically, if it wants to. Just so long as the object outlives the X
object that refers to it.
Then, the constructor of
X
, somehow, would defineY
using it
That is not possible. The Y
member would have to be statically typed at compile-time, ie as an A*
pointer or A&
reference. Unless X
is written as a template class, so that the user can then specify the actual derived type being passed in.
maybe creating a copy of
B:A
That is possible, but only if X
is templated, otherwise A
will have to declare a virtual clone()
method that all derived classes override to make copy of themselves.
The problem is that
X
doesn't know which derived type is passed and what size it is.
Polymorphism doesn't need to know that info.
CodePudding user response:
You could simply require the class to implement the copy operation (the Clone
function below):
class A
{
public:
virtual ~A() = default;
virtual std::unique_ptr<A> Clone() const = 0;
};
class B : public A
{
public:
std::unique_ptr<A> Clone() const override
{
return std::make_unique<B>(*this);
}
};
class X
{
public:
X(A const& param)
: abc(param.Clone())
{
}
// allow transferring an object stored in a unique_ptr to the object
template<class T> requires (std::is_base_of_v<A, T>)
X(std::unique_ptr<T>&& param)
: abc(std::move(param))
{}
private:
std::unique_ptr<A> abc;
};
Note that if you restrict the user to transferring the ownership to of the subclass of A
to X
, you don't need to Clone
functionality at all. You you could remove X::X(A const&)
and Clone
in this case. The user would still be able to create your object like this:
X x = std::make_unique<B>();
X x(std::make_unique<B>()); // alternative syntax for the above