让用户传递一个用户定义的类型作为参数类的子类来定义成员



以下是我设计的一些体系结构。我有一个类X,它有一个变量成员Y,它是指向类a的指针(或引用,我还没有决定(。类a是一个抽象类。X的用户可以从A(例如B:A(创建自己的派生类,并将其传递给X(可能在构造函数中(,X会以某种方式将其存储在Y中。

为了实现这一点,用户应该动态分配一个B:A类型的对象并将其传递给X。但是,对于用户来说,你能想出一种更简单的方法来做到这一点而不必调用new吗?理想情况下,我希望用户只需创建一个B:A类型的对象并传递它。然后,X的构造函数会以某种方式使用它定义Y(可能会创建B:A的副本(。问题是X不知道传递的派生类型和大小

我想创建一个单独的构造函数,用户可以将从a派生的任何类型作为参数传递到该构造函数,并将其转换为成员变量。它应该允许用户创建自己的类型来利用多态性。

class A {...};   // Abstract class (has pure virtual members)
class B : public A {...};   // Class defined by the user
class X
{
public:
X(A &param) { /* abc is defined */ }
A* abc;
}

我有一些想法:它能在a处重载纯虚拟副本分配运算符吗?在B:a处有一个成员来指定B:a的大小?然而,我仍然不知道如何让它发挥作用。

如何解决?还有更好的方法吗?提前谢谢。

要实现这一点,用户应该动态分配一个类型为B:A的对象,并将其传递给X。然而,对于用户来说,你能想出一种更简单的方法来做到这一点而不必调用new吗?

多态性不依赖于所使用的new。它只需要一个指向多态对象的指针/引用。如果调用方愿意,它可以静态地创建它的派生对象。只要该对象的寿命比引用它的X对象长即可。

然后,X的构造函数会以某种方式使用定义Y

这是不可能的。Y成员在编译时必须是静态类型的,即作为A*指针或A&引用。除非X是作为模板类编写的,这样用户就可以指定传入的实际派生类型

可能创建B:A的副本

这是可能的,但前提是X是模板化的,否则A将不得不声明一个所有派生类都覆盖的虚拟clone()方法来复制它们自己。

问题是X不知道传递了哪个派生类型以及它的大小

多态性不需要知道这些信息。

您可以简单地要求该类实现复制操作(下面的Clone函数(:

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;
};

请注意,如果您限制用户将A子类的所有权转移到X,则根本不需要Clone功能。在这种情况下,您可以删除X::X(A const&)Clone。用户仍然可以创建这样的对象:

X x = std::make_unique<B>();
X x(std::make_unique<B>()); // alternative syntax for the above

假设X类在传入后将拥有param,则可以执行以下操作:

#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> &param) : 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);
}

运行时,此代码将打印B destroyed。请注意,为了使其工作,A必须有一个虚拟析构函数。

如果要与调用方和/或其他对象共享param的所有权,请改用std::shared_ptr(但这会带来更多开销(。或者,正如@Remy所说,如果你能保证param的寿命超过x的寿命,你就可以存储一个原始指针(或引用(。


编辑:根据注释,X类构造函数的更好实现是:

X (std::unique_ptr <A> &&param) : m_param (std::move (param))  { }

然后你会这样称呼它:

X x { std::make_unique <B> (); }

这表明x拥有传入的对象

最新更新