'Let the user pass a user-defined type as sub-class of the parameter class to define a member
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.
Solution 1:[1]
To make this work, the user should dynamically allocate an object of type
B:Aand 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 defineYusing 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
Xdoesn't know which derived type is passed and what size it is.
Polymorphism doesn't need to know that info.
Solution 2:[2]
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
Solution 3:[3]
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).
Edit: As per the comments, a better implementation of the constructor of class X would be:
X (std::unique_ptr <A> &¶m) : m_param (std::move (param)) { }
And then you would call it like this:
X x { std::make_unique <B> (); }
This makes it clear that x owns the object passed in.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Remy Lebeau |
| Solution 2 | |
| Solution 3 |
