C++中explicit和extern的用法

今天把explicit和extern记混了,所以特此记录一下两个关键字的用法。extern是用于声明变量或函数的,而explicit用于类的单参构造函数,是禁止隐式类型转换的。

1. extern用法

首先C++中声明和定义是分开的,定义就要分配内存空间。因此,声明可以有多个,而定义只能有一个。对于变量,不带extern都是定义,比如int a;int a=1;都是定义;对于函数,带{}是定义,不带的就是声明。extern的作用就是告诉编译器,我这只是个声明,你去其他地方找定义,可以起到在文件间共享数据的作用。

C++中链接属性有三种:none(无)、external(外部)和 internal(内部)。
external,外部链接属性。非常量全局变量和自由函数(除成员函数以外的函数)均默认为外部链接的,它们具有全局可见性,在全局范围不允许重名。
internal,内部链接属性。具有该属性的类型有,const对象,constexpr对象,命令空间内的静态对象
none,在类中、函数体和代码块中声明的变量默认是具有none链接属性。它和internal一样只在当前作用域可见。

下面看extern的用法

1.1 extern用于变量

首先看用于变量,代码如下:

// main.cpp
int a;
int b = 200;

//test.cpp
int a;
int b = 100;

在这里插入图片描述
明显可以看到,报错a和b重定义。如果修改为:

// main.cpp
int a;
const int b = 200;

//test.cpp
int a;
const int b = 100; // 或者int b=100;

在这里插入图片描述
可以看到,只报错a重定义。这就说明常量全局变量默认是内部链接的,所以想要在文件间传递常量全局变量需要在定义时指明extern
而如下代码是可以运行的:

// main.cpp
#include<iostream>
int main()
{
	extern int a;
	a = 100;  // 或者直接写 int a=100;
	std::cout << a << std::endl; // 结果输出100
	return 0;
}

// test.cpp
int a=10;

说明局部变量不影响。下面看看extern的用法:

#include<iostream>
extern int a; // 声明全局变量
int main()
{
	std::cout << a << std::endl; // 输出a=10
	return 0;
}

// test.cpp
int a=10; // 声明并定义

刚才会发生错误,是因为a重定义。而使用extern声明后,就是告诉编译器,我这只是个声明,你去其他地方找定义,所以不会冲突,最后输出10。
extern用于函数差不多,具体例子可以看后面用extern "C"的用法

1.2 extern混合C语言

当C++中需要执行C风格的代码时,就需要用到extern “C”,先来看个例子:

// main.cpp
int func()
{
	return 1;
}

// test.cpp
int func()
{
	return 1;
}

在这里插入图片描述
明显看到,func函数重定义。再来看个带C语言的例子:

// main.cpp
#include<iostream>
int func()
{
	return 1;
}
int main()
{
	std::cout << func() << std::endl; // 输出1
	return 0;
}

// test.c
int func()
{
	return 2;
}

程序可以执行,输出1。说明编译器并没有把这两个函数当成同一个东西,也就说明C++和C语言风格的处理方式是不一样的。而两个C文件,也会冲突,如下:

// test.c
int func()
{
	return 2;
}

// test1.c
int func()
{
	return 2;
}

在这里插入图片描述
首先,从两个报错可以看出,C++重定义和C语言重定义报错方式是不一样的。C++的func函数处理成?func@@YAHXZ,而C语言处理成_func。这是因为C++支持函数重载,不能仅根据函数名区分,所以有@后面的一堆。这也就解释了刚才C语言定义的func函数在C++中输出1能够成功的原因,因为处理方式根本不一样。

现在来看在C++风格中调用C代码的例子:

// main.cpp
#include<iostream>
#include "test.h"
int main()
{
	std::cout << func() << std::endl;
	return 0;
}

// test.h
int func();
// test.c
int func()
{
	return 2;
}

在这里插入图片描述
可以看到,这既有声明,也有定义,怎么会报错呢?那是因为我们在main.cpp中是当作C++风格处理的,而实际上是按C风格处理的,可以看到报错显示的_func已定义,而_func就是C语言的处理风格。

解决方案:用extern "C"声明,这是一个C风格的,如下:

// test.h
extern "C" int func(); // 将test.h中的声明加上extern "C",其他不变
// 程序输出2

还有另一种方式,这种更推荐

// main.cpp
#include<iostream>
extern "C" 
{
	#include "test.h"
}
int main()
{
	std::cout << func() << std::endl;
	return 0;
}

// test.h
int func();
// test.c
int func()
{
	return 2;
}

这种方式直接对引用头文件的地方使用extern "C"声明,更方便。
extern “C” {}的形式还可以批量声明,例如

extern "C" 
{
	#include "a.h"
	#include "b.h"
	#include "c.h"
}

extern "C" 
{
	int f();
	int f1();
	int f2();
}

2. explicit

explicit主要用于类的仅有一个参数的构造函数,使用explicit的类就不能进行隐式转换。
先看隐式类型转换的例子:

class Point {
public:
	int x, y;
	Point(int x, int y = 0) :x(x),y(y) {}
	Point(const Point& p) {
		x = p.x;
		y = p.y;
	}
};
int main()
{
	//发生隐式类型转换 
	//编译器会将它变成如下代码 
	//tmp = Point(1,0) 
	//Point A(tmp); 
	//tmp.~Point(); 
	Point p = 1;  // 注意,因为构造函数虽然有两个参数,但有默认参数,所以依然发生了隐式类型转换
	Point pp = p; // 相当于隐式调用了拷贝构造
	return 0;
}

加上explicit后:

class Point {
public:
	int x, y;
	explicit Point(int x, int y = 0) :x(x),y(y) {}
	explicit Point(const Point& p) {
		x = p.x;
		y = p.y;
	}
};
int main()
{
	Point p = 1;  
	Point pp = p; 
	return 0;
}

在这里插入图片描述
可以看到编译器直接报错,也就是禁止了隐式类型转换。
要用如下方式:

int main()
{
	// 未加explicit之前,可以 Point p = Point(1);,加了只能这样显示调用构造函数
	Point p(1);  
	// 未加explicit之前,可以Point pp = Point(p); ,加了只能显示调用拷贝构造
	Point pp(p); 
	return 0;
}

所以explicit 关键字作用于单个参数的构造函数或者其他参数有默认值,作用就是禁止隐式类型转换。

  • 25
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值