Wiki
Function improvement
Existing English translations were reused. 44 current source blocks remain in Simplified Chinese, without triggering paid translation.
1.Function improvement
1.1.Function default parameters
In C++, parameters in a function's parameter list can have default values.
Syntax: 返回值类型 函数名 (参数= 默认值){}
Example:
int func(int a, int b = 10, int c = 10) {
return a + b + c;
}
//1. 如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值
//2. 如果函数声明有默认值,函数实现的时候就不能有默认参数
int func2(int a = 10, int b = 10);
int func2(int a, int b) {
return a + b;
}
int main() {
cout << "ret = " << func(20, 20) << endl;
cout << "ret = " << func(100) << endl;
return 0;
}
Operation result:
ret = 50
ret = 120
1.2.Function placeholder parameter
C++The formal parameter list of a function can include placeholder parameters to serve as placeholders, and the corresponding arguments must be supplied when calling the function.
Syntax: 返回值类型 函数名 (数据类型){}
At the current stage, placeholder parameters in functions have little significance, but this technique will be used in later lessons.
Example:
//函数占位参数 ,占位参数也可以有默认参数
void func(int a, int) {
cout << "this is func" << endl;
}
int main() {
func(10,10); //占位参数必须填补
return 0;
}
Operation result:
this is func
1.3.Function overloading
1.3.1.Function Overloading Overview
**Purpose:** Function names can be the same, improving reusability
Conditions for function overloading to be satisfied:
- In the same scope
- the function names are the same
- function parametersthe types are different or the number is different or the order is different
Note: The return value of a function cannot be used as a condition for function overloading
Example:
//函数重载需要函数都在同一个作用域下
void func()
{
cout << "func 的调用!" << endl;
}
void func(int a)
{
cout << "func (int a) 的调用!" << endl;
}
void func(double a)
{
cout << "func (double a)的调用!" << endl;
}
void func(int a ,double b)
{
cout << "func (int a ,double b) 的调用!" << endl;
}
void func(double a ,int b)
{
cout << "func (double a ,int b)的调用!" << endl;
}
//函数返回值不可以作为函数重载条件
//int func(double a, int b)
//{
// cout << "func (double a ,int b)的调用!" << endl;
//}
int main() {
func();
func(10);
func(3.14);
func(10,3.14);
func(3.14 , 10);
return 0;
}
Operation result:
func 的调用!
func (int a) 的调用!
func (double a)的调用!
func (int a ,double b) 的调用!
func (double a ,int b)的调用!
1.3.2.Precautions for function overloading
- Reference as an overload condition
- Function overloading encounters function default arguments
Example:
//函数重载注意事项
//1、引用作为重载条件
void func(int &a)
{
cout << "func (int &a) 调用 " << endl;
}
void func(const int &a)
{
cout << "func (const int &a) 调用 " << endl;
}
//2、函数重载碰到函数默认参数
void func2(int a, int b = 10)
{
cout << "func2(int a, int b = 10) 调用" << endl;
}
void func2(int a)
{
cout << "func2(int a) 调用" << endl;
}
int main() {
int a = 10;
func(a); //调用无const
func(10);//调用有const
//func2(10); //碰到默认参数产生歧义,需要避免
return 0;
}
Operation result:
func (int &a) 调用
func (const int &a) 调用
1.4.弱函数
C 和 C++ 都可以使用弱函数(Weak Function)机制,但需要编译器、目标平台和链接器支持。下面的示例采用 C++ 写法。
在支持弱符号的目标平台上,GCC、Clang 等编译器可以使用 __attribute__((weak)) 将一个函数定义为弱函数。
弱函数机制不属于标准 C 或标准 C++,
weak和__weak也不是这两种语言的标准关键字,而是工具链提供的扩展功能。
Syntax:
__attribute__((weak)) 返回值类型 函数名(参数列表)
{
// 默认实现
}
For example:
#include <iostream>
using namespace std;
__attribute__((weak)) void func()
{
cout << "weak func" << endl;
}
int main()
{
func();
return 0;
}
如果参与链接的目标文件中没有对应同一链接符号的普通函数定义(强定义),那么调用:
func();
时,就会使用这个弱函数。
1.4.1.弱函数的作用
弱函数最主要的作用是:
提供一个默认实现,同时允许用户在其他源文件中重新定义这个函数。
例如在 default.cpp 中:
#include <iostream>
using namespace std;
__attribute__((weak)) void func()
{
cout << "default func" << endl;
}
在另一个 user.cpp 中重新定义:
#include <iostream>
using namespace std;
void func()
{
cout << "user func" << endl;
}
此时两个源文件分别提供同一个函数的弱定义和强定义:
__attribute__((weak)) void func(); // 弱符号
void func(); // 普通符号,也叫强符号
将两个源文件编译生成的目标文件一起链接时,强定义会优先于弱定义,因此最终调用的是:
user func
可以简单理解为:
只有弱函数
↓
使用弱函数提供的默认实现
弱函数 + 普通函数
↓
普通函数优先
因此,弱函数常用于:
库提供默认实现
↓
用户需要时可以重新实现
↓
不需要修改库本身的源代码
1.4.2.弱函数使用条件
以下机制在 C 和 C++ 中都可使用;实际支持情况还取决于目标文件格式、目标平台和链接器。
| 编译器 | 弱函数支持 | 推荐写法 | __attribute__((weak)) |
__weak |
|---|---|---|---|---|
| GCC | ✅ | __attribute__((weak)) |
✅ | ❌ 原生没有,通常通过宏定义实现 |
| Clang / LLVM | ✅ | __attribute__((weak)) |
✅ | ❌ 一般不是原生关键字 |
| ARMCC 5 | ✅ | __weak |
⚠️ 不作为主要写法 | ✅ 原生支持 |
| ArmClang / ARM Compiler 6 | ✅ | __attribute__((weak)) |
✅ | ⚠️ 可通过兼容头文件或宏提供 |
| MSVC | ⚠️ 无直接弱函数属性,可通过链接器机制实现类似功能 | /alternatename 等 |
❌ | ❌ |
1.4.3.弱函数和函数重载的区别
弱函数与前面学习的函数重载并不是同一种机制。
函数重载:
void func();
void func(int a);
void func(double a);
这些函数的参数列表不同,因此它们本身就是不同的函数。
而弱函数:
__attribute__((weak)) void func()
{
}
和另一个文件中的:
void func()
{
}
参数列表、函数名等都是相同的。
它们表示的是同一个链接符号的弱实现和强实现。
因此可以简单区分:
函数重载
↓
编译阶段根据参数选择不同函数
弱函数
↓
链接阶段在弱符号和强符号之间选择
1.4.4.C 和 C++ 混合时的弱函数
需要已经学会C 接口以及 C/C++ 混合工程的头文件
如果弱函数本身是按照 C 的方式编译的,例如在 .c 文件中:
__attribute__((weak)) void func(void)
{
}
那么在 .cpp 文件中重新定义这个函数时,需要使用:
extern "C" void func(void)
{
// 用户实现
}
这是因为 C++ 默认会对函数名进行名称修饰,而:
extern "C"
表示按照 C 的链接方式生成函数符号,从而保证两个函数对应同一个符号。
如果弱函数本身就在 .cpp 中,并且使用普通的 C++ 链接方式:
__attribute__((weak)) void func()
{
}
那么在另一个 .cpp 中重新定义时直接写:
void func()
{
}
即可,不需要添加:
extern "C"
Therefore:
是否需要 extern "C"
↓
取决于原函数使用 C linkage 还是 C++ linkage
↓
与 weak 本身没有直接关系
| 弱函数来源 | 弱函数链接方式 | C++ 中强定义 |
|---|---|---|
.c |
C linkage | extern "C" void foo() |
.cpp 普通函数 |
C++ linkage | void foo() |
.cpp + extern "C" |
C linkage | extern "C" void foo() |
1.4.5.__weak
在一些工程中还可能看到:
__weak void func()
{
}
__weak 在 C 和 C++ 中的含义取决于工具链:ARMCC 5 将它作为原生扩展关键字;在使用 GCC 等编译器的工程中,它也可能只是对编译器属性进行了一层宏封装,例如:
#define __weak __attribute__((weak))
在采用上述宏定义的工程中:
__weak void func()
{
}
In fact, this is equivalent to:
__attribute__((weak)) void func()
{
}
这样做主要是为了:
- 让代码更加简洁;
- 隐藏具体的编译器属性写法;
- 方便工程根据不同编译器统一处理弱符号语法。
例如 STM32 的部分工程中就可能通过这种宏定义来使用 __weak。
Summary:
标准 C 和标准 C++ 本身都没有 weak 或 __weak 关键字
C 和 C++ 都可使用工具链提供的弱函数扩展
GCC / Clang 等编译器(目标平台支持时):
__attribute__((weak))
ARMCC 5:
__weak
MSVC 没有直接对应的弱函数属性,可通过链接器机制实现类似功能。
弱函数:
提供默认实现
↓
没有强定义 → 使用弱函数
有强定义 → 使用强函数
.c 中的弱函数
↓
.cpp 中重新定义时通常需要 extern "C"
.cpp 中的 C++ 弱函数
↓
.cpp 中重新定义时不需要 extern "C"