派生类实例共享同一基类实例
Derived class instances to share the same base class instance
假设我有一个具有以下接口的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();
问题
我希望dp1
和dp2
共享同一基类实例
我之所以需要它,是因为Proc::m_counter
在两个处理器中必须相同,也就是说,在一个处理器中更改其值必须被另一个处理器"看到"。当然,Proc::m_counter
只是我想分享的一个变量的例子
解决该问题的标准方法是什么?
备注
我不想快速解决这个问题。含义:
- 不希望制作
Proc::m_counter
static
。我仍然需要将它绑定到特定的Proc
实例 - 我不想将
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
(而不是dp1
或dp2
)来删除对象。如果您给Proc
一个虚拟析构函数,那么您可以使用其中的任何一个(并且只能使用一个)。
除非你正确理解,否则我不建议使用虚拟继承。因此,由于你必须放松其中一个要求,我通常认为标准方法是放松第一个:将计数存储在两个对象之外的一个单独对象中。
不要使用继承。相反,给DerivedProc1
和DerivedProc1
一个指向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);
这两个优点:-保留了接口。-计数器数据已被共享(不做静态,它是特定于实例的)。
希望这能有所帮助。
- 在C++中,是否可以基于给定的标识符创建基类的新实例,反之亦然
- 没有实例的基类?
- 从基类实例调用派生类方法而不进行强制转换
- 为什么派生类的实例从基类调用方法?
- 从模板基类派生是否在派生类声明的点实例化模板
- 是否可以从派生类返回基类的实例?
- 为什么不能在实例化对基类的引用的同时实例化指向派生类的指针?
- 选择专用于派生实例的基类的类模板
- 基类中向量的干净实例化
- 在C++中,为什么仅包含与其基类实例的联合的派生类占用的内存多于联合的大小?
- 为什么我无法使用受保护/私有继承访问派生实例中基类的受保护成员?
- 使用单一实例类作为派生类时,如何访问基类中的函数
- 为什么模板名称在派生类中可用(基类是模板的实例)?
- 如何为抽象基类创建模板实例化?
- 在C++中,有没有办法区分基类指针指向的实例类?
- 从基类堆栈调用派生类实例
- 如何将指针存储在实例变量中,该指针被声明为指向基类的指针
- 声明基类类型的指针,但随后通过指向子类来实例化它.这是良好的编程实践吗?
- 派生实例不是指针时的基类的异构容器
- C++用于深层继承的模板单一实例基类