【问题标题】:struct as a base class - C/C++ interoperabilitystruct 作为基类 - C/C++ 互操作性
【发布时间】:2013-12-05 16:14:31
【问题描述】:

我记得我在某处看到过一些代码,这些代码曾经将结构作为基类,将 C++ 类作为派生类

struct Base_Struct
{

}

class Derived : Base_Struct
{
  ...
}

关键是指向 Base_Struct* 的指针从 C++ 文件传递​​到一些 C 文件,然后这些文件设法使用 Base_Struct 中的一些函数指针。

我的问题是:如果我将 Base_Struct* 传递给 C 文件,C 代码是否能够完全使用 Base_Struct?派生类呢?

【问题讨论】:

  • 我对类结构分配的理解是,基类在前,被驱动的类成员在基类之后存储。这应该有效。但我不是语言律师,所以不要相信我的话。结构数组可能不起作用,因为它们比 Base_Struct 大。

标签: c++ c class oop struct


【解决方案1】:

如果我将Base_Struct* 传递给C 文件,C 代码是否能够完全使用Base_Struct

如果它是标准布局类,并且 C 编译器对此类类使用与 C++ 编译器相同的 ABI,则它可以访问所有数据成员。显然,它不能访问任何成员函数或静态成员,因为这些东西在 C 中不存在,并且必须从任何与 C 兼容的结构定义中排除。

派生类呢?

你不能在 C 程序中定义那个类,所以它不能用指针做任何有趣的事情。

【讨论】:

  • 其中“标准布局类”表示Plain Old Data,即可以由 C 编译器编译的东西。如果它使用任何 C++ 特性(私有/受保护成员、虚函数或它具有父类/结构),则它不是 POD。
  • @AdamRosenfield:不,“标准布局类”是指“标准布局类”。 POD 是标准布局的一个特例,有更多的限制。
  • 从 C API 扩展结构的主要原因(至少对我而言)是为它们提供一个默认构造函数,它将它们的字段清零,也许还有一个析构函数......
  • @MikeSeymour:感谢您的更正。今天之前我没有听说过“标准布局类”这个词,但显然它是 C++11 中添加的一个新概念(这些天我只使用 C++03,因为我使用的一些编译器没有还支持 C++11)。
【解决方案2】:

也许它可以用于基类。但这是非常特殊的情况:

  • POD 结构
  • 同样的包装
  • 如果您在 C 和 C++ 中分配/取消分配,则运行时相同
  • 纯 C 语法
  • 等等……

但它永远不会直接用于派生类。

但这会产生很多限制,以至于我会提供一个 C 级 API 来分配一个操作那些在“void*”上定义为别名的指针(我什至不确定你是否可以在纯 C 中做这样的别名)。

类似:

 typedef void* BaseStructPtr;

 BaseStructPtr AllocateBase(/* constructor params */);
 BaseStructPtr AllocateDerived(/* constructor params */);

 TypeOfField1 GetField1(BaseStructPtr ptr);
 TypeOfField2 GetField2(BaseStructPtr ptr);

 /* etc... */

【讨论】:

    【解决方案3】:

    在您的示例中,是的,C 编译器将能够使用 Base_Struct* 指针和访问成员。您可以在 C 和 CPP 文件中使用相同的结构定义而不会出现问题。只要是 Cpp 为 C 文件转换结构,编译器就会完成这项工作。

    void Give_Typed_Pointer_to_C_Function(Base_Struct* pStruct) {...}
    void Give_Pointer_to_C_Function(void* pStruct) {...}
    Base_Struct A;
    Derived B;
    Give_Typed_Pointer_to_C_Function(&A);         // OK
    Give_Typed_Pointer_to_C_Function(&B);         // OK 
    Give_Pointer_to_C_Function(&A);               // OK    
    Give_Pointer_to_C_Function(&B);               // wrong
    Give_Pointer_to_C_Function((Base_Struct*)&B); // OK 
    

    为此,您必须使用相同的对齐和打包结构,但是当您传递指针时,我想您是同一个项目,所以应该已经是这种情况了。

    如果你保持结构的顺序相同,你甚至可以做更多的事情

    struct S1 { int x; 
                int y;
              };
    class C1 {  int x;
                int y;
                int add(int,int); 
              };
    void Give_Typed_Pointer_to_C_Function(S1 *pStruct);
    C1 object;
    Give_Typed_Pointer_to_C_Function((S1*)&object);  // OK
    
    // an safer alternative as you can't have to be sure that S1 and C1 match
    struct C1 { int x;
                int y;
    #ifdef __cplusplus
                int add(int,int); 
    #endif
              };
    

    如果您将 Cpp 关键字(如 class/public/private 和方法)放入带有定义的结构中。如果你不使用继承,它也可以工作。

    【讨论】:

      【解决方案4】:

      我认为从 C 中声明的结构派生没有任何问题:

      C/C++

      #include <stdio.h>
      
      #ifdef __cplusplus
      extern "C" {
      #endif
      
      typedef struct {
          int a;
          int b;
      } X;
      
      void print(struct StructX* x) {
          printf("%d, %d\n", x->a, x->b);
      }
      
      #ifdef __cplusplus
      } // extern "C"
      #endif
      

      C++

      #include <iostream>
      
      struct Y : StructX {
          int c;
      
          Y()
          : c(3)
          {
              a = 1;
              b = 2;
          }
      
          void print() {
              ::print(this);
              std::cout << c << std::endl;
          }
      };
      
      
      int main() {
          Y y;
          y.print();
      }
      

      在 C++ 中声明结构会使事情复杂化。 现在结构可能(或将要)拥有 C 不知道的信息并且(可以说) memcpy 可能会引入未定义的行为。为避免这种情况,您必须 将 C++ 结构的功能包装在 C 中:

      C/C++

      #ifdef __cplusplus
      extern "C" {
      #endif
      
      typedef struct Z; // An opaque type (for X) to ensure minimum type safety.
      
      extern int a(Z*);
      extern int b(Z*);
      
      #ifdef __cplusplus
      } // extern "C"
      #endif
      

      C++

      extern "C" {
          int a(Z* z) {
              return ((X*)z)->a;
          }
          int b(Z* z) {
              return ((X*)z)->b;
          }
      } // extern "C"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-12-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-12
        • 2017-02-18
        相关资源
        最近更新 更多