“穿越层次结构” - 为什么不呢?

发布于 2025-01-26 12:40:11 字数 1290 浏览 3 评论 0原文

我们有一个多个继承层次结构:

// A  B
// \ /
//  C
//

ab都是抽象类。 C实际上是一个模板类,因此,如果您拥有b,并且要以a访问其成员,那么降低的降低是不可能的。

所有a's and b必须为c's,因为这是层次结构中唯一的具体类。因此,所有a的s必须为b's,反之亦然。

我正在尝试快速调试一些我需要访问b的,我需要访问a :: name of。我无法降低c,因为我不知道它的模板类型。因此,我正在编写下面的代码,令人惊讶的是它行不通。我想知道什么给了。

struct A { virtual void go() = 0; std::string name; };
struct B { virtual void go() = 0; };
struct C : A, B { void go() override { } };

int main()
{

    C c;
    c.name = "Pointer wonders";
    puts(c.name.c_str()); // Fine.
    B* b = (B*)&c;
    //puts(b->name.c_str()); // X no from compiler.

    A* a1 = (A*)&c;
    puts(a1->name.c_str()); // As expected this is absolutely fine

    // "Cross" the hierarchy, because the B really must be a __C__, because of the purely virtual functions.
    // neither A nor B can be instantiated, so every instance of A or B must really be a C.    
    A* a2 = (A*)b;
    puts(a2->name.c_str()); // Why not??

    // If you downcast first, it works
    C* c2 = (C*)b;
    A* a3 = (A*)c2;
    puts(a3->name.c_str()); // fine

}

We have a multiple inheritance hierarchy:

// A  B
// \ /
//  C
//

Both A and B are abstract classes. C is actually a templated class, so downcasting is near impossible if you have a B and you want to access a member of it as an A.

All A's and B's must be C's, since that is the only concrete class in the hierarchy. Therefore, all A's must be B's, and vice versa.

I'm trying to debug something quickly where I have a B that I need to access A::name of. I can't downcast to C because I don't know the templated type of it. So I'm writing code like below and surprisingly it doesn't work; and I'm wondering what gives.

struct A { virtual void go() = 0; std::string name; };
struct B { virtual void go() = 0; };
struct C : A, B { void go() override { } };

int main()
{

    C c;
    c.name = "Pointer wonders";
    puts(c.name.c_str()); // Fine.
    B* b = (B*)&c;
    //puts(b->name.c_str()); // X no from compiler.

    A* a1 = (A*)&c;
    puts(a1->name.c_str()); // As expected this is absolutely fine

    // "Cross" the hierarchy, because the B really must be a __C__, because of the purely virtual functions.
    // neither A nor B can be instantiated, so every instance of A or B must really be a C.    
    A* a2 = (A*)b;
    puts(a2->name.c_str()); // Why not??

    // If you downcast first, it works
    C* c2 = (C*)b;
    A* a3 = (A*)c2;
    puts(a3->name.c_str()); // fine

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

潜移默化 2025-02-02 12:40:11

首先,停止使用C样式铸造。如果您做错了什么,则编译器不会抱怨(C样式铸件通常在多个继承中不起作用)。

示例中导致运行时间错误的任何铸件都不会使用static_cast编译。虽然键入更长的时间,但是当使用不当时,您会立即获得反馈,而不是未定义的行为,这些行为有时会损坏数据并在使用数据时很久就会引起问题。

作为A和B包含虚拟函数,您可以轻松地使用dynamic_cast而不知道C。如果您知道C,则可以使用static_cast到C,如果您知道有一个派生的C一定。但是,为什么不使用虚拟功能,也不使用兄弟姐妹之间的任何交叉呢?

其不起作用的原因是因为C风格铸件可以执行以下任何铸件:

  • static_cast
  • reinterperet_cast
  • const_cast,

如果丢失了类的定义,则C样式铸件将进行Reinterpret_cast。您还需要非常谨慎使用void *,因为您必须转换回原始类型。

作为简化的规则,您可以想象C铸造就像进行单个static_cast(已知的孩子或父级或int类型)或reinterpret_cast < /code>(未知类型,而不是父母/子类),然后是const_cast,如有必要。

c * - &gt; void * - &gt; B *将无法使用任何C或C ++铸件。

这种铸件无效的主要原因是编译器在执行铸件时必须调整指针,并且使用多个继承。这需要考虑到A和B零件以不同的偏移启动。

另外,您可以在B中添加虚拟函数a * geta()= 0,并以C实现它以具有自己的浏览方式。如果未知并且必须禁用RTTI(对于嵌入式系统上的ex。),则可以选择。

老实说,您应该避免多次继承和铸造,因为它使代码更难维护,因为它会增加班级之间的耦合,并且在将两者混合在一起时可能很难特别发现错误。

First of all, stop using C style cast. The compiler won't complain if you do something wrong (C style cast usually do not works in multiple inheritance).

Any cast that cause run-time error in you example would not compile with a static_cast. While it is a bit longer to type, you get instant feedback when used improperly instead of undefined behavior that will sometime corrupt data and cause problem long afterward when that data is use.

As A and B contains virtual function, you can easily use dynamic_cast without knowing C. If you know C, you could use static_cast to C if you know there is a derived C for sure. But why not use virtual functions and not do any crossing between siblings?

The reason it does not works is because C-style cast can do any of the following cast:

  • static_cast
  • reinterperet_cast
  • const_cast

Also, C style cast will do a reinterpret_cast if the definition of a class is missing. You also need to be very careful with void *as you must convert back to original type.

As a simplified rule, you can imagine that C cast is like doing either a single static_cast (known child or parent class or primitive types like int) or reinterpret_cast (unknown type, not a parent/child class) followed by a const_cast if necessary.

C * --> void * --> B * won't work with any C or C++ cast.

Th primary reason that such cast don't works is that the compiler must adjust this pointer when doing a cast and multiple inheritance is used. This is required to take into account that the A and B part start at a distinct offset.

Alternatively, you can add a virtual function A * GetA() = 0 in B and implemente it in C to have your own way to navigate. That can be an option if is unknown and RTTI must be disabled (for ex. on embedded systems).

Honestly, you should avoid multiple inheritance and casting as it make the code harder to maintain as it increase coupling between classes and it can cause hard to find bug particularily when mixing both together.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文