派生类实例共享同一基类实例

Derived class instances to share the same base class instance

本文关键字:实例 基类 共享 派生      更新时间:2023-10-16

假设我有一个具有以下接口的Proc类:

class Proc
{
public:
void process();
protected:
virtual void do_process() = 0;
private:
int m_counter;
};

现在,假设我有两个实现Proc接口的派生类。

class DerivedProc1:
public Proc
{
protected:
virtual void do_process();
};
class DerivedProc2:
public Proc
{
protected:
virtual void do_process();
};

现在我生成两个派生类作为:

Proc* dp1 = new  DerivedProc1();
Proc* dp2 = new  DerivedProc2();

问题

我希望dp1dp2共享同一基类实例
我之所以需要它,是因为Proc::m_counter在两个处理器中必须相同,也就是说,在一个处理器中更改其值必须被另一个处理器"看到"。当然,Proc::m_counter只是我想分享的一个变量的例子
解决该问题的标准方法是什么?

备注

我不想快速解决这个问题。含义:

  1. 不希望制作Proc::m_counterstatic。我仍然需要将它绑定到特定的Proc实例
  2. 我不想将m_counter保持在Proc之外,并将其作为对proc的引用

如前所述,这是不可能的。以下要求相互矛盾:

  • 计数是DerivedProc1实例的数据成员,而不是"在Proc之外">
  • 该计数是DerivedProc2实例的数据成员
  • DerivedProc1的实例和DerivedProc2的实例是通过对new的单独调用来分配的,也就是说,它们是不同的大多数派生对象

不同的派生对象不能共享一个数据成员。原因是一个派生程度最高的对象被定义为占用自己的内存区域。只有同一派生对象的子对象才能共享成员。

如果你愿意放松"存储在外部"的限制,那么解决方案是"显而易见的":让每个对象都持有一个shared_ptr,用于包含计数的对象。将相同的计数持有对象传递给你想要共享计数的任何对象的构造函数。唯一让这个问题变得困难的是你对计数在内存中的位置的限制。

如果你愿意放宽"单独的new调用"的限制,那么你可以使用虚拟继承,并将两个Proc对象构造为一个最派生对象的基类子对象:

class DerivedProc1 : public virtual Proc { ... };
class DerivedProc2 : public virtual Proc { ... };
class CombinedProc : public DerivedProc1, DerivedProc2 { ... };
CombinedProc *dp0 = new CombinedProc();
DerivedProc1 *dp1 = dp0;
DerivedProc2 *dp2 = dp0;

由于这些类都没有虚拟析构函数,因此必须使用dp0(而不是dp1dp2)来删除对象。如果您给Proc一个虚拟析构函数,那么您可以使用其中的任何一个(并且只能使用一个)。

除非你正确理解,否则我不建议使用虚拟继承。因此,由于你必须放松其中一个要求,我通常认为标准方法是放松第一个:将计数存储在两个对象之外的一个单独对象中。

不要使用继承。相反,给DerivedProc1DerivedProc1一个指向Proc实例的成员指针,并用指向单个Proc对象的指针构造这两个对象。

类似这样的东西:

class DerivedProc1
{
protected:
Proc *proc;
void do_process(){
proc->process();
}
public:
DerivedProc1(Proc *proc):proc(proc){}
};

可能最好使用std::shared_ptr<Proc>作为派生类成员,而不是原始指针。

与其有一个带有共享资源的基类,不如有一个只用于接口的基类,然后是一个在类之间共享的单独对象,该对象包含计数器。

首先,do_process必须是virtual,而DerivedProcX需要从Proc派生。

要共享计数器,您可以将其声明为static,并将基类作为模板,类似于以下内容:

class BaseProc {};
template <int n> class Proc : BaseProc
{
virtual void do_process() = 0;
static int m_counter;
}
template <int n> class DerivedProc1 : public Proc<n>
{
void do_process() {}
}
template <int n> class DerivedProc2 : public Proc<n>
{
void do_process() {}
}
int main()
{
// dp1 and dp2 share the same counter
Proc<1>* dp1 = new DerivedProc1<1>;
Proc<1>* dp2 = new DerivedProc2<1>;
// dp3 and dp4 share the same counter, but not the same as dp1 and dp2
BaseProc* dp3 = new DerivedProc1<2>;
BaseProc* dp4 = new DerivedProc2<2>;
}

当然,这个解决方案假设您在编译时知道哪些实例需要共享同一计数器。

您应该重新考虑您的设计。

根据我的说法,你需要一个由继承和组合组成的混合设计(如下所示)。

class CounterHolder
{
public:
int m_counter;
};
class Proc
{
public:
Proc(CounterHolder *counterHolder)
{
m_conterHolder = counterHolder;
}
void process();
protected:
virtual void do_process() = 0;
private:
CounterHolder *m_counterHolder;
};
class DerivedProc1: public Proc
{
public:
DerivedProc1(CounterHolder *counterHolder) : Proc(counterHolder);
protected:
virtual void do_process();
};
class DerivedProc2: public Proc
{
public:
DerivedProc2(CounterHolder *counterHolder) : Proc(counterHolder);
protected:
virtual void do_process();
};

现在,您有继承以及共享数据(由于组合)的好处

CounterHolder *counterHolder = new CounterHolder();
Proc* dp1 = new  DerivedProc1(counterHolder);
Proc* dp2 = new  DerivedProc2(counterHolder);

这两个优点:-保留了接口。-计数器数据已被共享(不做静态,它是特定于实例的)。

希望这能有所帮助。