extern、关于C++的变量和类的声明和定义
extern
参考:extern声明变量详解
变量的声明:
int data; // 这样既声明了data同时也定义了data
extern int data; // 只声明而不定义
函数的声明:
void hello(); // 函数的声明
extern void hello(); // 也是函数的声明,和void hello(); 一模一样。 参考:https://blog.csdn.net/weixin_38145317/article/details/86496041
// extern void hello(); 仅仅是暗示这个函数可能在别的源文件里定义。
// 如果在函数定义的地方带有关键字extern,表示该函数会提供给外部文件使用,
// 其实有些编译器是默认每个函数都是extern类型的,反之是static类型。参考:https://developer.aliyun.com/article/638079
extern声明全局变量:
- 头文件中不允许定义全局变量,因为当头文件中定义了全局变量A,则每一个包含了头文件的源文件中会定义一次全局变量A,使得整个代码空间中多次定义了此全局变量A,编译时会出现重定义的错误。
- 一般会在头文件中使用extern对全局变量进行声明,这样其他包含此头文件的源文件就可以使用此全局变量。头文件中只进行了声明,所以必须有且仅有一个源文件中对全局变量进行定义。
使用变量之前必须声明,声明可以有多次,而定义只能有一次。
extern "C"
extern“C”的作用(最重点)
- extern "C"的真实目的是实现类C和C++的混合编程。extern “C”是由C++提供的一个连接交换指定符号,用于告诉C++这段代码是C函数。extern “C”后面的函数不使用的C++的名字修饰,而是用C。这是因为C++编译后库中函数名会变得很长,与C生成的不一致,造成C++不能直接调用C函数。
- C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为:void foo(int x, int y);该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。C++提供了C连接交换指定符号extern“C”来解决名字匹配问题。
- 被extern "C"限定的函数或变量是extern类型的;extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。被extern "C"修饰的变量和函数是按照C语言方式编译和连接的。
- 与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern “C”修饰。
extern "C":让此c++代码按照c语言的方式编译。这应该就导致了此c++代码中的函数不能进行重载操作。
c++代码中使用c库
// cheader.h
#ifndef _C_HEADER_
#define _C_HEADER_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdlib.h>
int add(int a,int b);
#ifdef __cplusplus
}
#endif
#endif
// cfunc.c
//#include "header.h"
int add(int a,int b)
{
return a+b;
}
//main.cpp
#include <iostream>
#include "cheader.h"
using namespace std;
int main()
{
int a,b=0;
b= add(2,3);
cout<<b<<endl;
return 0;
}
编译:
gcc -c cfunc.c -o libadd.a # 生成c静态库
g++ main.cpp libadd.a # C++中使用c静态库。
如果cheader.h中没有使用extern "C"
,上述编译过程会出错。
c代码中使用c++库
// cheader.h头文件
#ifndef _C_HEADER_
#define _C_HEADER_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdlib.h>
int sub(int a,int b);
#ifdef __cplusplus
}
#endif
#endif
//cppsub.cpp
#include "cheader.h"
int sub(int a,int b)
{
return a-b;
}
/*但是如果将头文件的那一行注释掉,也会在连接时出错,因为在头文件中已经说明使用C语言的方式编译,但是不说明这一点,会使用C++编译方式进行
*/
//cmain.c
#include "cheader.h"
int main()
{
int a=0,b=0;
b = sub(2,3);
printf("b is %d\r\n",b);
return 0;
}
__cplusplus的作用:如果是C++文件(*.cpp)后缀,则使用extern “C”。
【这里为什么写了两个#ifdef __cplusplus……#endif
??】答:cmain.c中使用#include "cheader.h"
可以将#include <stdio.h>
和#include <stdlib.h>
包含进来。从这点我们可以知道,两个#ifdef __cplusplus……#endif
使得#ifdef __cplusplus……#endif
并没有作用到#include <stdio.h>
和#include <stdlib.h>
上。
编译:
g++ -c cppsub.cpp -o libsub.a # 生成c静态库
gcc cmain.c libsub.a # C++中使用c静态库。
如果cheader.h中没有使用extern "C"
,上述编译过程会出错。
类的声明和定义
定义一个类时,对于其中的数据成员,不论有无默认值,也不论是static还是非static的,我们只是描述了一个类,注意,仅仅是描述了类,也即,我们只知道它含有哪些类型,如果还定义了函数成员,我们还描述了这种类型的操作。所以类中的成员变量都只是被声明了,并未被定义。
当类中包含static修饰的成员变量,那么就必须在源文件中对static变量进行定义,如下:
// test.h
#pragma once
class A{
public:
static int a; // 声明static成员变量
...
};
// test.cpp
#include"test.h"
int A::a; // 定义static成员变量
int main(){
return 0;
}