【问题标题】:Making C++ Classes inherit from C Structs, Recommended?使 C++ 类继承自 C 结构,推荐吗?
【发布时间】:2011-10-07 00:18:50
【问题描述】:

我最近在做一些 Windows Api 编码(仍在做)。我试图找到将 WNDCLASSEX 包装到 C++ 类中的最佳方法,当我有这个疯狂的想法时,WNDCLASSEX 是一个结构,对吗? (即使它是用 c 编写的)并且在 C++ 中,结构被视为类,所以我为什么不将我的 WinCLass 声明为 WNDCLASSEX 的派生,所以我尝试了:

 class WinClass : protected WNDCLASSEX

它奏效了!然后我尝试将它与 SDL 结构一起使用,但那些也有效。但是当我从它们派生类时,某些结构(尤其是 SDL 结构)要么不编译,要么导致无法解释的运行时错误。所以我的问题: 是否推荐使用这种 C 结构?它实际上是由专业人士使用的,还是只是一个蹩脚的黑客?我应该将其用于我的包装器或应用程序,还是只会引入无法解释的错误?

【问题讨论】:

  • 如果您对 Win32 包装器感兴趣,您可能想看看 existing libraries 是如何设计的。 ATLWTL 可能特别有趣。此外,RSWL 在它还处于起步阶段时看起来很有趣,但我最近没有听到太多关于它的消息(比如它被命名为 RSWL :)
  • 在这种情况下,我个人会使用组合而不是继承(即class WinClass { WNDCLASSEX wcex; }; 而不是class WinClass : protected WNDCLASSEX {};)。在这种情况下使用组合似乎更明智。

标签: c++ class struct


【解决方案1】:

只要不开始添加虚拟成员,应该没问题。如果确实添加了虚拟成员,则虚拟表指针可能会与结构的内存布局发生冲突。

这包括虚拟析构函数!

【讨论】:

  • 只要你正确地向下投射(即隐式投射或static_cast),虚拟应该不是问题......
  • 如果WinClass 被类型转换为WNDCLASSEX,为什么虚拟会成为问题?
  • @bdonlan,是的,但是 win32 api 不知道 可以放弃。它需要一个指向结构开头的指针。
  • @Ajay,因为指向 C++ 中的结构的指针与指向 C 中的结构的指针(这就是 win32 api 的含义)相同。由于 vtable 指针烘焙到结构中,因此 C++ 结构可以在实际指针之后开始。 C++ 编译器知道如何提取正确的信息,但已经用 C 编译的代码不知道如何处理它。
  • 所以,你的意思是:在派生类中拥有虚函数会在基类中放入一些原本不存在的东西。你的意思是如果我分配一个派生类,对象切片将/不会发生,但是基类/结构中的这个额外填充会被放入?我不相信。
【解决方案2】:

C++ 中类和结构的唯一区别在于访问说明符

对于类,访问说明符默认为私有。
对于结构,访问说明符默认为公共。

这意味着如果你有一个从其他类/结构派生的类,默认情况下它将是私有继承 &
如果你有一个从其他类/结构派生的结构,默认情况下它将是一个公共继承

但一个问题是,由于结构的默认访问说明符是公共的,它们可能会暴露可能不应该暴露给派生类的成员。从这个意义上说,整个封装具有一个全新的维度。当然,如果您可以更改您打算用作 Base 的结构中的访问说明符,则可以避免该问题,但这可能是不可能的,因为这可能会影响从其他部分使用该结构的方式程序。

编辑:
通过 cmets,我现在有一个公平的想法,您指的是什么错误,这些错误并不是因为您是从结构派生的,而是因为您误解了继承和访问说明符的规则。

这是一个示例程序用于演示:

#include<iostream>

using namespace std;

struct FirstStruct
{
    private:
        int a;
    public:
        int b;
        FirstStruct():a(10),b(20),c(30)
        {
        }
    protected:
        int c;
};

class MyClass:protected FirstStruct
{
    public: 
        int i;
        MyClass():i(40),j(50),k(60)
        {
        }
        void doSomething()
        {
            a = 100; //private in Base is not accessible in Derived
            b = 100; //Accessible
            c = 100; //Accessible  

            i = 100; //Accessible  
            b = 100; //Accessible  
            c = 100; //Accessible  
        }
    private:
        int j;
    protected:
        int k; 
};

int main()
{
    MyClass obj;
    obj.i = 100; //Accessible
    obj.j = 100; //Error Cannot be Accessed, j is private member
    obj.k = 100; //Error Cannot be Accessed, k is protected member
    obj.a = 100; //Error Cannot be Accessed, a is private member in Base Class
    obj.b = 100; //Error Cannot be Accessed; 
                 //b is protected member of MyClass, because of protected Inheritance
    obj.c = 100; //Error Cannot be Accessed; 
                 //same reason as b


    obj.doSomething();

    return 0;
}

您可以在 Ideone 上查看运行相同的 here
即使将 Base 的类型从 struct FirstStruct 更改为 class FirstStruct here,您也会注意到同样的错误(仅此而已)。

建议您看看这个答案here,以清除您对继承和访问说明符规则的理解。

【讨论】:

  • One problem though is that since for structure default access specifier is public 我有一个解决方案。如果我像使用 protected 关键字一样继承结构,: protected Cstruct 所有成员都会受到保护。这仅适用于 C 结构(我认为)。虽然我不知道为什么会这样,你呢?
  • @burningprodigy:您能否发布您所指案例的简约样本?恐怕Not working 并没有真正讲述这个场景。
  • 你的意思是我所说的关于 SDL 结构的内容吗?
  • @burningprodigy:为什么不使用简单的结构和类组合来制作一个简约的**独立**(不依赖于任何外部 sdk 等)示例来演示问题?
  • 示例:(已测试)struct x { int i; }; class d : protected x {}; 现在位于 main d myobj; myobj.i = 10; // Will not work , inaccessible because it is protected (try it)
【解决方案3】:

我对 Win API 没有太多经验,但我知道在 C++ 中,类和结构之间的唯一区别是默认可见性。对于类,它是私有的,对于结构,它是公共的。换句话说,一个类可以是一个结构的子类。

请记住,为任何基类声明虚拟析构函数是一种很好的做法,因此您要子类化的结构不会拥有它。

【讨论】:

    猜你喜欢
    • 2011-04-04
    • 1970-01-01
    • 1970-01-01
    • 2015-12-12
    • 1970-01-01
    • 2014-12-22
    • 1970-01-01
    • 1970-01-01
    • 2011-06-16
    相关资源
    最近更新 更多