我有一个从另外两个(虚拟)类继承的类。让我们调用我的两个超类A和B。我可以有只从A继承的对象,只从B继承的对象,以及从A和B继承的对象。现在,我希望在某个容器中存放已知从A和B继承的对象:为C/Java语法mashup道歉,但这可能是这样的:std::vector<? : A, B>
。现在,这是不存在的,但是事实证明,保存我的容器的类主要使用这些类的"A“功能,这就是我目前使用std::vector<A*>
的原因。
然而,在我的代码中的另一个地方,在另一个地方,在不同的地方,访问相同的向量,但目的是使用"B“功能。
虽然我个人知道我已经确保了添加到我的向量中的所有实例都是从A和B扩展的,但是编译器没有。这显然使我很难访问B功能。
因此,我的问题是:我如何从A到B“旁白”?我有一个指向A
的指针,它的动态类型类似于C
(即,继承自A和B-但不一定是特定的C),但我希望获得指向B
的指针,显然不需要更改动态类型。
dynamic_cast做了这项工作,但我认为这是一种丑陋的做法。有更好的主意吗?
发布于 2021-03-28 15:00:49
进行侧播的方法是使用dynamic_cast
。(你甚至可以对此进行测试。前往cppreference.com搜索“侧播”。只有一次命中。dynamic_cast
将使用它的动态魔力来确保指向对象实际上继承了新类(例如,B
),并动态地确定需要向指针中添加什么偏移量来执行侧广播。
我不认为dynamic_cast
方法“丑陋”,但我确实发现它缺乏优雅。可以接受,但不是很想要。我更倾向于一种更结构化的方法,因为有人断言所讨论的对象都是从A
和B
继承的。(如果不是因为这个断言,dynamic_cast
可能是正确的方法。)
在某个容器中,有一个容器保存已知从A
和B
继承的对象。这样的物品有名字吗?如果是这样的话,这可能是一个新类的基础,称为AB
,它继承了A
和B
。它可能只会继承这两个类,但它仍然可以在容器中发挥有用的作用。如果容器要保存指向AB
的指针,那么容器将自动记录所有元素必须同时从A
和B
派生的文档。此外,不需要动态检查类型,也不需要处理侧广播失败。编译器将强制要求对象必须同时从A
和B
派生,才能首先添加到容器中。此外,不需要动态计算偏移量,因为该偏移量被放入AB
中。总体结果是代码更干净,在编译时进行更多的检查。我更喜欢这个解决方案。
OP的一条评论认为,这个提议的AB
“基本上是空的,只能充当包装器,这有点难看”。我不同意“丑陋”这一说法,尽管我并不是说这种观点是错误的。不过,我要指出,这是有先例的。就公共接口而言,std::iostream
基本上是完全空的,只用作std::istream
和std::ostream
的包装器。它的作用类似于我对AB
的建议。欢迎您认为std::iostream
是丑陋的,但这是无可否认的标准。
发布于 2021-03-28 15:37:40
不能保证A
和B
在对象中的地址是相同的,甚至对于所有的C
、D
、E
、F
,这些地址之间的差异也是一样的。这意味着您需要为向量的每个元素提供额外的数据,这将告诉您如何获得指向基类A和B的指针。
dynamic_cast
从虚拟表中提取这些附加数据(假设编译器通过虚拟表实现RTTI )。
优点:
缺点:
dynamic_cast
可能有些慢(如果它是通过遍历类层次结构实现的)。可能的替代办法:
1.适配器
class Adapter {
public:
template<class C>
/* implicit */ Adapter(C* pointer):
pointer_a_(pointer),
pointer_b_(pointer) {
static_assert(std::is_base_of_v<A, C> && std::is_base_of_v<B, C>,
"C should be derived from both A and B.");
}
A* getA() const { return pointer_a_; }
B* getB() const { return pointer_b_; }
private:
A* pointer_a_ = nullptr;
B* pointer_b_ = nullptr;
};
int main() {
std::vector<Adapter> my_vector;
C1 c1;
C2 c2;
my_vector.emplace_back(&c1);
my_vector.emplace_back(&c2);
for (Adapter& elem : my_vector) {
elem->getA()->foo();
elem->getB()->bar();
}
}
优点:
A
和B
派生的。缺点:
2.添加虚拟函数B* A::getB()。
class A {
public:
// Return this (casted to B*), or nullptr if the dynamic object is not derived from B.
virtual B* getB() { return nullptr; }
// ...
};
class C : public FooWhichInheritsB, public A {
public:
B* getB() override { return this; }
};
int main() {
std::vector<A*> my_vector;
C1 c1;
C2 c2;
my_vector.emplace_back(&c1);
my_vector.emplace_back(&c2);
for (A* elem : my_vector) {
elem->foo();
elem->getB()->bar();
}
}
优点:
getB()
比执行dynamic_cast
更快。缺点:
getB()
比简单地读取Adapter
实现中的pointer_b_
成员要慢。C
,D
,E
,F
中重写D
,.3.引入一个公共基类AB
class AB : public A, public B {};
class C : public AB { /* ... */ };
class D : public AB { /* ... */ };
优点:
缺点:
class Foo : public A2, public B {};
类型的对象(其中A2
是从A
派生的)。https://stackoverflow.com/questions/66844580
复制