3

在诸如此类的问题中,只要所有成员的类型相同,顺序相同,并且没有声明任何虚拟成员,就尽可能解释 C++ 类/结构和 C 结构之间的兼容性

那是我的问题。我有虚拟方法,我非常希望在用 C++ 操作结构时保留它们。

让我们来看看这个玩具示例。它是在单个头文件中定义的与 C 和 C++ 兼容的结构。

mystr.h:

#ifdef __cplusplus
#include <string>
struct mystr_base {
    virtual ~mystr_base() {}

    virtual std::string toString() = 0;
};
#endif

#ifdef __cplusplus
extern "C" {
#endif

struct mystr
#ifdef __cplusplus
: public mystr_base
#endif
{
    const char* data;

#ifdef __cplusplus
    std::string toString() {
        return std::string(data);
    }
#endif
};

#ifdef __cplusplus
}
#endif

这可能并不完全漂亮,但可以用于示例。在实际场景中,C 和 C++ 变体可能位于不同的标头中,而 C++ 结构扩展了 POD 结构。无论实施如何,对齐问题仍然存在。

在此示例中,如果编写的 C 程序将 的实例传递mystr给 C++ 函数,则 vtable 将干扰对齐:

测试.h:

#include "mystr.h"

#ifdef __cplusplus
extern "C"
#endif
void mycxxfunc(struct mystr str);

测试.cpp:

#include <stdio.h>
#include "test.h"

void mycxxfunc(mystr str) {
    printf("mystr: %s\n", str.data);
}

主.c:

#include "test.h"

int main(int argc, char** argv) {
    const char* testString = "abc123";
    struct mystr str;
    str.data = testString;
    mycxxfunc(str);
}

$ g++ -c test.cpp && gcc main.c test.o
$ ./a.out
Segmentation fault (core dumped)

(假设这是因为 C++ 函数试图data从结构分配内存的末尾之外读取)

启用这种 C-C++ 互操作性同时仍保留在 C++ 中使用虚函数的能力的最佳方法是什么?

4

1 回答 1

2

我不建议您将头文件与#ifdefs.

在这种情况下,如果您想同时保留某种虚拟化和 C 兼容性,您应该做的第一件事是:

  1. 使您的 C++ 和 C 类型成为表示的不透明指针。
  2. 将实现细节放在 .cpp 文件中。

一个想法随之而来。

头文件:

struct MyStrImpl;

struct MyStr {
   MyStrImpl * impl;
};

extern "C" MyReturnType myFunction(MyStr myStr);

文件中的实现.cpp

struct MyCppString {
    virtual ...
};

#ifdef __cplusplus
struct MyStrImpl : public MyCppString {

};
#else
struct MyStrImpl {

};
#endif
MyStr::MyStr() : impl{new MyStrImpl{}} {

}

这样你就有了一个可以在 C 和 C++ 中使用的类型。

优点:

  • 头文件中没有#ifdef
  • 与 C/C++ 兼容,可从两种语言中使用。

缺点:

  • 由于外部“C”而失去过载。
  • 必须使用 C 风格的接口(但可以在 .cpp 文件中使用类似 C++ 的虚函数接口来实现。

您不能同时在头文件中同时拥有与 C 兼容的类型和虚函数,而不会将其与#ifdef.

于 2017-11-15T17:10:07.453 回答