ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

c – 铸造Derived **→Base **错了吗?有什么选择?

2019-08-27 22:06:51  阅读:196  来源: 互联网

标签:vtable c containers casting multiple-inheritance


上下文

我的目标是拥有一个包含和操作几个基类对象的基本容器类,然后是一个包含和操作多个派生类对象的派生容器类.根据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相关 – 如果没有虚拟方法,似乎不会发生错误).你可以run it heresee 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 **也是如此),并且将一个转换为另一个在任何方面都是不合法的.
>从/派生的指针强制转换不一定是微不足道的.如果你有结构X:A,B {},那么指向B base的指针将不同于指向A base的指针†(并且在游戏中有vtable,也可能与指向X的指针不同).它们必须是,因为(子)对象不能驻留在同一个内存地址.在转换指针时,编译器将调整指针值.如果您(尝试)强制转换指针数组,这当然不会/不会发生.

如果你有一个指向Derived的指针数组并希望获得指向Base的指针数组,那么你必须手动转换每个指针.由于两个数组中的指针值通常都不同,因此无法“重用”同一个数组.

†(除非满足empty base optimization的条件,否则不适用).

标签:vtable,c,containers,casting,multiple-inheritance
来源: https://codeday.me/bug/20190827/1745190.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有