本篇是上一篇的继续。
9. extern “C” 9.1 特点- 被extern限定的函数或变量是extern类型的
- 被extern "C"修饰的变量和函数是按照C语言方式编译和连接的
extern "C"的作用是让C++编译器将extern "C"声明的代码当作C语言代码处理,可以避免C++因符号修饰导致代码不能和C语言库中的符号进行链接的问题。
由于c++有重载函数名的功能,而C没有,c++编译器不能仅仅使用函数名作为链接到的唯一id,所以它通过添加有关参数的信息来破坏函数名。C编译器不需要修改名称,因为在C中不能重载函数名。当你在c++中声明一个函数具有extern“C”链接时,c++编译器不会向用于链接的名称添加实参/形参类型信息。
正如您所知,您可以明确地为每个单独的声明/定义指定 extern “C” 链接,或者使用块将一系列声明/定义分组以具有特定链接:
extern "C" void foo(int);
extern "C"
{
void g(char);
int i;
}
你会经常在C头文件中看到这样的代码:
#ifdef __cplusplus
extern "C" {
#endif
// all of your legacy C code here
#ifdef __cplusplus
}
#endif
这样做的结果是它允许您在 C++ 代码中使用该 C 头文件,因为将定义宏“__cplusplus”。但是您仍然可以将它与未定义宏的C 代码一起使用,因此它不会看到唯一的 C++ 构造。
9.2 C++与C编译区别在C++中常在头文件见到extern "C"修饰函数,那有什么作用呢? 是用于C++链接在C语言模块中定义的函数。
C++虽然兼容C,但C++文件中函数编译后生成的符号与C语言生成的不同。因为C++支持函数重载,C++函数编译后生成的符时带有函数参数类型的信息,而C则没有。
例如int add(int a, int b)函数经过C++编译器生成.o文件后,add会变成形如add_int_int之类的, 而C的话则会是形如_add, 就是说:相同的函数,在C和C++中,编译后生成的符号不同。
这就导致一个问题:如果C++中使用C语言实现的函数,在编译链接的时候,会出错,提示找不到对应的符号。此时extern "C"就起作用了:告诉链接器去寻找_add这类的C语言符号,而不是经过C++修饰的符号。
9.3.C++调用C函数C++调用C函数的例子: 引用C的头文件时,需要加extern "C"
//add.h
#ifndef ADD_H
#define ADD_H
int add(int x,int y);
#endif
//add.c
#include "add.h"
int add(int x,int y) {
return x+y;
}
//add.cpp
#include
#include "add.h"
using namespace std;
int main() {
add(2,3);
return 0;
}
编译:
//生成 add.o 文件 gcc -c add.c
链接:
g++ add.cpp add.o -o main
没有添加extern “C” 报错:
> g++ add.cpp add.o -o main add.o:在函数‘main’中: add.cpp:(.text+0x0): `main'被多次定义 /tmp/ccH65yQF.o:add.cpp:(.text+0x0):第一次在此定义 /tmp/ccH65yQF.o:在函数‘main’中: add.cpp:(.text+0xf):对‘add(int, int)’未定义的引用 add.o:在函数‘main’中: add.cpp:(.text+0xf):对‘add(int, int)’未定义的引用 collect2: error: ld returned 1 exit status
添加extern "C"后:
add.cpp
#includeusing namespace std; extern "C" { #include "add.h" } int main() { add(2,3); return 0; }
编译的时候一定要注意,先通过gcc生成中间文件add.o。
gcc -c add.c
然后编译:
g++ add.cpp add.o -o main9.4 C中调用C++函数
extern "C"在C中是语法错误,需要放在C++头文件中。
// add.h
#ifndef ADD_H
#define ADD_H
extern "C" {
int add(int x,int y);
}
#endif
// add.cpp
#include "add.h"
int add(int x,int y) {
return x+y;
}
// add.c
extern int add(int x,int y);
int main() {
add(2,3);
return 0;
}
编译:
g++ -c add.cpp
链接:
gcc add.c add.o -o main
综上,总结出使用方法,在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern "C"声明,在.c文件中包含了extern "C"时会出现编译语法错误。所以使用extern "C"全部都放在于cpp程序相关文件或其头文件中。
10.explicit(显式)关键字- explicit 修饰构造函数时,可以防止隐式转换和复制初始化
- explicit 修饰转换函数时,可以防止隐式转换,但按语境转换除外
#include11.fiend友元 11.1概述using namespace std; struct A { A(int) { } operator bool() const { return true; } }; struct B { explicit B(int) {} explicit operator bool() const { return true; } }; void doA(A a) {} void doB(B b) {} int main() { A a1(1); // OK:直接初始化 A a2 = 1; // OK:复制初始化 A a3{ 1 }; // OK:直接列表初始化 A a4 = { 1 }; // OK:复制列表初始化 A a5 = (A)1; // OK:允许 static_cast 的显式转换 doA(1); // OK:允许从 int 到 A 的隐式转换 if (a1); // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换 bool a6(a1); // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换 bool a7 = a1; // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换 bool a8 = static_cast (a1); // OK :static_cast 进行直接初始化 B b1(1); // OK:直接初始化 // B b2 = 1; // 错误:被 explicit 修饰构造函数的对象不可以复制初始化 B b3{ 1 }; // OK:直接列表初始化 // B b4 = { 1 }; // 错误:被 explicit 修饰构造函数的对象不可以复制列表初始化 B b5 = (B)1; // OK:允许 static_cast 的显式转换 // doB(1); // 错误:被 explicit 修饰构造函数的对象不可以从 int 到 B 的隐式转换 if (b1); // OK:被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B 到 bool 的按语境转换 bool b6(b1); // OK:被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B 到 bool 的按语境转换 // bool b7 = b1; // 错误:被 explicit 修饰转换函数 B::operator bool() 的对象不可以隐式转换 bool b8 = static_cast (b1); // OK:static_cast 进行直接初始化 return 0; }
友元提供了一种 普通函数或者类成员函数 访问另一个类中的私有或保护成员 的机制。也就是说有两种形式的友元:
(1)友元函数:普通函数访问某个类中的私有或保护成员。
(2)友元类:类A中的成员函数访问类B中的私有或保护成员
优点:提高了程序的运行效率。
缺点:破坏了类的封装性和数据的透明性。
总结:- 能访问私有成员- 破坏封装性- 友元关系不可传递- 友元关系的单向性- 友元声明的形式及数量不受限制
11.2 用例子理解 C++ 中的友元函数- 1.C++中友元函数的语法
您可以在类的主体内使用关键字“friend”来声明友元函数。
class Box {
double width;
public:
double length;
friend void printWidth ( Box box ) ;
void setWidth ( double wid ) ;
} ;
-
2.C++中友元函数的特点
- 友元函数像普通函数一样被调用,并可以在公共或私有部分声明。
- 它不在将其声明为友元函数的类的作用域内。
- 它必须使用对象名和带有成员名的点成员操作符来访问成员名。
-
3.C++ 友元函数示例
显示友元函数工作的简单程序(1):
#includeusing namespace std; class Distance { private: int meter; // friend function friend int addFive(Distance); public: Distance() : meter(0) {} }; // friend function definition int addFive(Distance d) { //accessing private members from the friend function d.meter += 5; return d.meter; } int main() { Distance D; cout << "Distance: " << addFive(D); return 0; }
显示友元函数工作的简单程序(2):
使用友元函数对两个不同类对象进行add操作:
#includeusing namespace std; // forward declaration class ClassB; class ClassA { public: // constructor to initialize numA to 12 ClassA() : numA(12) {} private: int numA; // friend function declaration friend int add(ClassA, ClassB); }; class ClassB { public: // constructor to initialize numB to 1 ClassB() : numB(1) {} private: int numB; // friend function declaration friend int add(ClassA, ClassB); }; // access members of both classes int add(ClassA objectA, ClassB objectB) { return (objectA.numA + objectB.numB); } int main() { ClassA objectA; ClassB objectB; cout << "Sum: " << add(objectA, objectB); return 0; }
显示友元函数工作的简单程序(3):
使用友元函数打印一个box的长度:
#includeusing namespace std; class Box { private: int length; public: box(): length ( 0 ) { } friend int printLength(Box); // friend function }; int printLength (Box b) { b.length += 10; return b.length; } int main() { Box b; Cout << “Length of box:” <
- 11.3用示例代码理解 C++ 中的友元类
- 1.在 C++ 中实现友元类的语法
实现友元类的语法是:class ClassB; class ClassA { // ClassB is a friend class of ClassA friend class ClassB; ... .. ... } class ClassB { ... .. ... }正如您在语法中看到的,您需要做的就是在类前面使用关键字friend 使其成为友元类。使用此语法将使 ClassB 成为 ClassA 的友元类。由于 ClassB 成为友元类,它将可以访问 ClassA 的所有公有、私有和受保护成员。然而,反过来就不成立了。那是因为 C++ 只允许授予友元关系而不允许接受它。因此,ClassA 将无法访问 ClassB 的私有成员。
- 2.C++ 友元类的例子
现在您已经了解了在 C++ 中实现友元类的语法及其使用,请查看一些示例以更好地理解该概念。
示例 1:访问其他类的私有成员的简单示例#includeusing namespace std; class Exmp_A{ int i =3; // Declaring the friend class friend class Exmp_B; }; class Exmp_B { public: void display(Exmp_A &a) { cout<<"The value of i is : "< 示例 2:使用 C++ 中的友元类执行计算
在本例中,您将类ExmpB声明为友元类,ExmpB执行一个sum操作,该操作将类ExmpB中的私有成员变量与类ExmpA的私有成员变量进行相加。#include参考目录using namespace std; class ExmpA{ private: int x; // Declaring the friend class friend class ExmpB; public: // Initializing x value using a constructor ExmpA() : x(9) {} }; class ExmpB{ private: int y; public: // Initializing y value using a constructor ExmpB() : y(13) {} // Function to perform addition int sum(){ ExmpA a; return a.x + y; } }; int main(){ ExmpB b; cout << "Sum is: " << b.sum(); return 0; } https://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777432.html
https://www.bookstack.cn/read/CPlusPlusThings/ec48d16ca2a6afb0.md
https://www.simplilearn.com/tutorials/cpp-tutorial/friend-class-in-cpp



