没有数据成员的派生类的引用提升
Posted
技术标签:
【中文标题】没有数据成员的派生类的引用提升【英文标题】:Reference promotion for a derived class with no data members 【发布时间】:2021-12-01 10:48:55 【问题描述】:我有一个有趣的问题,涉及我维护的库中的类层次结构。一个非常简化的情况视图如下:
class Base
// private data + public interface to said data
;
class ClassA : public Base
// Behaviour
;
class ClassB : public Base
// Behaviour
;
所以这里我有一个类,它私有地包含数据并且具有一致的接口。实际上,这是一个具有许多不同存储模型的模板类。两个派生类ClassA
和ClassB
纯粹添加了相同行为的不同实现,并且不包含任何数据。 应该可以在不调用任何副本的情况下将对ClassA
实例的引用转换为ClassB
之一。当然可以使用
ClassA a;
B& a_bref = *reintepret_cast<B*>(&a);
但这违反了所有规则。我的问题:有没有一种安全的方法来实现这样的转换运算符?
【问题讨论】:
声明并实现cast operator 等一下...没有副本?不,ClassA
不是ClassB
。它们在Base
中有一些共同点,仅此而已。两者都可以用作Base
,但ClassA
和ClassB
在内存中可能有截然不同的签名,因此在ClassA
中寻找ClassB
特定的东西从一开始就注定要失败。随着时间的推移,一些限制正在减弱,您可以利用Standard Layout classes 之间的相似之处,这些类足够简单,可以满足要求,但这主要是因为它们不包含任何内容。
遗憾的是,我认为考虑这种结构的正确方法不是根据类层次结构,而是作为为数据存储类实现的特征系统(参见 Rust 模型)
您处于“可能会起作用”的情况之一,因为您没有更改子类中的数据,但只需要一位程序员稍后出现即可完全搞砸。您可能想查看Visitor pattern。对于我对您的用例的理解,这似乎是一个不错的方向。
其实,如果类有虚函数,reinterpret_cast 帮不了你,对象还是会引用实际类的vtable。
【参考方案1】:
多重继承
实现目标的一种方法是通过多重继承。
class A : virtual public Base
//...
;
class B : virtual public Base
//...
;
class AorB : public A, public B
//...
;
使用虚拟继承,您只有一个 Base
实例,该实例在构成 AorB
的 A
和 B
之间共享。这种方法意味着您需要创建一个知道您可能想要翻阅哪些子类型的类。这对您来说可能是也可能不是问题,具体取决于您的用例。
策略
更灵活的方法可能是将A
和B
视为策略,将Base
视为上下文。在这种方法中,您不会使用继承。您可以将数据与接口分离,这样A
和B
可以继承访问器方法,但引用数据。
class Base
friend class Base_Interface;
//...
;
class Base_Interface
Base &context_;
//...
Base_Interface (Base &context) : context_(context)
template <typename X> operator X () return context_;
;
class A : public Base_Interface
//...
A (Base &context) : Base_Interface(context)
;
class B : public Base_Interface
//...
B (Base &context) : Base_Interface(context)
;
也许懒惰地,有一个模板转换方法允许Base_Interface
的用户转换为在其构造函数中接受Base
的其他类。如果Base
没有公共成员,而Base_Interface
是它唯一的朋友,则可以解决。
【讨论】:
以上是关于没有数据成员的派生类的引用提升的主要内容,如果未能解决你的问题,请参考以下文章