上下文
我的目标是有一个包含和操作几个基类对象的基容器类,然后有一个包含和操作几个派生类对象的派生容器类。根据 this answer 的建议,我试图通过让每个指针都包含一个指针数组(一个 Base**
和一个 Derived**
),并从 Derived**
转换来做到这一点至 Base**
在初始化容器基类时。
但是,我遇到了一个问题——尽管编译得很好,但在操作包含的对象时,我会出现段错误或调用错误的方法。
问题
我已将问题归结为以下最小情况:
#include <iostream>
class Base1 {
public:
virtual void doThing1() {std::cout << "Called Base1::doThing1" << std::endl;}
};
class Base2 {
public:
virtual void doThing2() {std::cout << "Called Base2::doThing2" << std::endl;}
};
// Whether this inherits "virtual public" or just "public" makes no difference.
class Derived : virtual public Base1, virtual public Base2 {};
int main() {
Derived derived;
Derived* derivedPtrs[] = {&derived};
((Base2**) derivedPtrs)[0]->doThing2();
}
您可能希望这会打印“Called Base2::doThing2
”,但是……
$ g++ -Wall -Werror main.cpp -o test && ./test
Called Base1::doThing1
的确——代码调用Base2::doThing2
,但是Base1::doThing1
最终被调用。我也遇到过更复杂类的段错误,所以我假设它是与地址相关的 hijinks(可能与 vtable 相关 - 如果没有 virtual
方法,错误似乎不会发生)。你可以run it here , 和 see the assembly it compiles to here .
You can see my actual structure here – 它更复杂,但它将它与上下文联系起来并解释了为什么我需要类似的东西。
为什么 Derived**
→ Base**
Derived*
时转换无法正常工作→ Base*
做,更重要的是,将派生对象数组作为基对象数组处理的正确方法是什么(或者,如果做不到,另一种创建容器类的方法可以包含多个派生对象)?
我无法在向上转换(((Base2*) derivedPtrs[0])->doThing2()
)之前建立索引,恐怕,因为在完整代码中数组是一个类成员——而且我不确定转换是不是一个好主意(甚至可能)在容器类中使用包含对象的每个地方手动。不过,如果是处理此问题的方法,请纠正我。
(我认为这在这种情况下没有什么不同,但我所处的环境中 std::vector
不可用。)
编辑:解决方案
许多答案表明,单独转换每个指针是拥有一个可以包含派生对象的数组的唯一方法——事实似乎确实如此。不过,对于我的特定用例,我设法使用模板 解决了问题!通过为容器类应该包含的内容提供类型参数,而不是必须包含派生对象的数组,数组的类型可以在编译时设置为派生类型(例如BaseContainer<Derived> container(length, arrayOfDerivedPtrs);
).
Here's a version of the broken "actual structure" code above, fixed with templates.
最佳答案
有很多因素使这段代码变得非常糟糕并导致了这个问题:
为什么我们首先要处理双星类型?如果
std::vector
不存在,为什么不自己写一个?不要使用 C 风格的转换。您可以将指向完全不相关的类型的指针相互转换,并且编译器不允许阻止您(巧合的是,这正是这里发生的事情)。使用
static_cast
/dynamic_cast
相反。假设我们有
std::vector
, 为了便于记法。您正在尝试转换std::vector<Derived*>
至std::vector<Base*>
.这些是不相关的类型(对于Derived**
和Base**
也是如此),将一个转换为另一个在任何方面都是不合法的。从/到 derived 的指针转换不一定是微不足道的。如果你有
struct X : A, B {}
, 然后是指向B
的指针base 将不同于指向A
的指针base† (并且有一个 vtable 在起作用,也可能不同于指向X
的指针)。它们必须是,因为(子)对象不能驻留在相同的内存地址。当您转换指针时,编译器将调整指针值。如果您(尝试)转换指针数组,这当然不会/不能发生在每个单独的指针上。
如果你有一个指向 Derived
的指针数组并希望获得指向 Base
的指针的数组,那么你必须手动转换每一个。由于两个数组中的指针值通常不同,因此无法“重复使用”同一个数组。
†(除非满足 empty base optimization 的条件,但您不是这种情况)。
关于c++ - 转换 Derived** → Base** 是错误的吗?还有什么选择?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54632425/