在C

时间:2017-11-15 16:54:17

标签: c++ c virtual-functions memory-layout vptr

在诸如this之类的问题中,只要所有成员具有相同的类型,顺序相同,并且没有声明虚拟成员,就可以解释C ++类/结构和C结构之间的兼容性。 / p>

那是我的问题。我有虚方法,我非常想在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将干扰对齐:

test.h:

#include "mystr.h"

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

TEST.CPP:

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

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

main.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 ++互操作性的最佳方法是什么?

1 个答案:

答案 0 :(得分:2)

我不建议您使用#ifdefs混乱头文件。

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

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

    标题文件:

    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风格的界面(但可以使用C ++实现 - 就像.cpp文件中的虚函数界面一样。

    您不能同时在头文件中同时使用C兼容类型和虚函数,而不会使用#ifdef使其混乱,这是我不建议的,因为如果您需要重构代码会很不方便