extern C不能在班级使用?

时间:2009-06-22 03:11:46

标签: c++ c visual-studio-2008 extern

只想在Windows环境下确认VSTS 2008 + C ++项目,我们只能将extern C应用到功能级别,不能应用于类级别(以便所有成员函数从类中使用C语言名称修改) ?我尝试了几种方法,但总是编译错误。

提前谢谢, 乔治

5 个答案:

答案 0 :(得分:12)

您可以通过非常复杂(但完全合法)的黑客攻击将extern "C"应用于成员函数:

extern "C" typedef int bar_t(int x);

struct foo {
     bar_t bar; // yes, this declares a nonstatic member function!
};

int foo::bar(int x) { return x; } // definition

根据ISO C ++ 03 9.3 [class.mfct] / 9:

,这是可能的
  

可以使用typedef为函数类型声明(但未定义)成员函数。结果成员函数与明确提供函数声明符时的类型完全相同,见8.3.5。

然而,由于ISO C ++ 03 7.5 [dcl.link] / 4:

,这并没有真正为你买任何东西
  

对于类成员和成员函数的名称,将忽略C语言链接   类成员函数的类型。

答案 1 :(得分:5)

extern“c”使用c风格的链接;也就是说,原始函数名称是从库中暴露的内容。因为它只是一个原始函数名,所以只有C ++的功能才能使用它,包括名称空间,类,结构或联合中的方法或外部数据成员。

澄清:结构和联合在C中,但没有成员函数,因此它们在C ++中的成员函数不能以c风格导出(并且不需要导出结构和联合定义,因为它已经在标题中)

答案 2 :(得分:4)

我不敢。但是如果你想将C ++的对象传递给C函数,你可以参考这个链接:http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html#faq-32.8

答案 3 :(得分:4)

查看您在之前的答案中发表的评论(“[问]是我们是否可以在班级应用extern C,以便班级中的所有函数自动具有C样式名称修改?”,答案是'extern "C"并不是那么有效。'

从语法上讲,extern "C"可以应用于卷曲分隔块的单个语句:

extern "C" int my_foo(int i)
{
    ...
}

extern "C" {
    int my_bar(int i)
    {
        ...
    }

    int my_baz(int i)
    {
        ...
    }
}

在整个C标头上使用extern "C"和适当的#ifdef __cplusplus警卫是很常见的。

从语义上讲,应用extern "C"的实际效果仅适用于“普通”(即非类)函数和指向函数的指针。当然,您无法将其应用于C ++模板。你也不能将它应用于类方法(因为类方法需要知道调用它的对象,并且C风格的链接没有任何方法将该信息传递给函数)。

可以对命名空间中存在的函数应用extern "C",但是当通过C使用时,命名空间信息将会消失。


更新

如果您已经有一个类(我们将使用POD class来简化),并且您希望从C中使用它,则需要将extern "C"应用于可调用的函数C.不幸的是,即使在简单的情况下,这也会变得丑陋:

// in the header file
#ifdef __cplusplus
namespace X {
#endif
    struct A
    {
        int x;
#ifdef __cplusplus
        A() : x(5) { }
        int foo()
        {
             return x += 5;
        }
#endif
    };
#ifdef __cplusplus
    extern "C" {
#endif
        int A_foo(struct A a);
        struct A A_create();
#ifdef __cplusplus
    }
}
#endif


// in the .cc file
#include "try.h"

namespace X {
    extern "C" {
        int A_foo(A* a)
        {
            return a.foo();
        }

        A A_create()
        {
            return A();
        }
    }
}

// in the .c file
#include <stdio.h>
#include "try.h"

int main()
{
    struct A a = A_create();
    printf("%d", A_foo(a));
}

使用gcc你可以按如下方式编译:

  • C ++文件:g++ try.cc -c -o try.o
  • C档案:gcc try.c try.o

有几个要点:

  • 如果你的C ++文件在幕后调用STL,或者调用newdelete(或new[]delete[]),你需要链接最终的程序到C ++运行时库(gcc中的命令行开关是-lstdc++
  • 在编译C和C ++代码时,你可能想要传递相同的优化标志(优化会影响对象的大小,如果大小不匹配,你会遇到很多麻烦) 。同上多线程。
  • 您可以在C ++代码中使用所有您想要的例外,但once they cross C code all bets are off
  • 如果你想要更复杂的东西,你可能想看看PIMPL模式。
  • 当一个结构体超出C代码范围时,C ++析构函数被而不是调用(某些编译器可能会保证这样做,但它不是标准的)。如果你需要对这些对象进行任何清理,你需要调用一个调用析构函数的extern“C”函数。

明确调用析构函数:

extern "C" void A_destroy(struct A a)
{
    a.~A();
}

答案 4 :(得分:2)

嗯...... extern "C"强制C风格的联系。它不能用于AFAIK类。