以下是我设计的一些体系结构。我有一个类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 ¶m) { /* 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> ¶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);
}
运行时,此代码将打印B destroyed
。请注意,为了使其工作,A
必须有一个虚拟析构函数。
如果要与调用方和/或其他对象共享param
的所有权,请改用std::shared_ptr
(但这会带来更多开销(。或者,正如@Remy所说,如果你能保证param
的寿命超过x
的寿命,你就可以存储一个原始指针(或引用(。
编辑:根据注释,X
类构造函数的更好实现是:
X (std::unique_ptr <A> &¶m) : m_param (std::move (param)) { }
然后你会这样称呼它:
X x { std::make_unique <B> (); }
这表明x
拥有传入的对象