Wiki
函數提高
本文由簡體中文內容確定性轉換,並受版本化術語表保護。
1.函數提高
1.1.函數默認參數
在C++中,函數的形參列表中的形參是可以有默認值的。
語法: 返回值类型 函数名 (参数= 默认值){}
示例:
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;
}
運行結果:
ret = 50
ret = 120
1.2.函數佔位參數
C++中函數的形參列表裏可以有佔位參數,用來做佔位,調用函數時必須填補該位置
語法: 返回值类型 函数名 (数据类型){}
在現階段函數的佔位參數存在意義不大,但是後面的課程中會用到該技術
示例:
//函数占位参数 ,占位参数也可以有默认参数
void func(int a, int) {
cout << "this is func" << endl;
}
int main() {
func(10,10); //占位参数必须填补
return 0;
}
運行結果:
this is func
1.3.函數重載
1.3.1.函數重載概述
**作用:**函數名可以相同,提高複用性
函數重載滿足條件:
- 同一個作用域下
- 函數名稱相同
- 函數參數類型不同 或者 個數不同 或者 順序不同
注意: 函數的返回值不可以作為函數重載的條件
示例:
//函数重载需要函数都在同一个作用域下
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;
}
運行結果:
func 的调用!
func (int a) 的调用!
func (double a)的调用!
func (int a ,double b) 的调用!
func (double a ,int b)的调用!
1.3.2.函數重載注意事項
- 引用作為重載條件
- 函數重載碰到函數默認參數
示例:
//函数重载注意事项
//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;
}
運行結果:
func (int &a) 调用
func (const int &a) 调用
1.4.弱函數
C 和 C++ 都可以使用弱函數(Weak Function)機制,但需要編譯器、目標平台和鏈接器支持。下面的示例採用 C++ 寫法。
在支持弱符號的目標平台上,GCC、Clang 等編譯器可以使用 __attribute__((weak)) 將一個函數定義為弱函數。
弱函數機制不屬於標準 C 或標準 C++,
weak和__weak也不是這兩種語言的標準關鍵字,而是工具鏈提供的擴展功能。
語法:
__attribute__((weak)) 返回值类型 函数名(参数列表)
{
// 默认实现
}
例如:
#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"
所以:
是否需要 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()
{
}
實際上等價於:
__attribute__((weak)) void func()
{
}
這樣做主要是為了:
- 讓代碼更加簡潔;
- 隱藏具體的編譯器屬性寫法;
- 方便工程根據不同編譯器統一處理弱符號語法。
例如 STM32 的部分工程中就可能通過這種宏定義來使用 __weak。
總結:
标准 C 和标准 C++ 本身都没有 weak 或 __weak 关键字
C 和 C++ 都可使用工具链提供的弱函数扩展
GCC / Clang 等编译器(目标平台支持时):
__attribute__((weak))
ARMCC 5:
__weak
MSVC 没有直接对应的弱函数属性,可通过链接器机制实现类似功能。
弱函数:
提供默认实现
↓
没有强定义 → 使用弱函数
有强定义 → 使用强函数
.c 中的弱函数
↓
.cpp 中重新定义时通常需要 extern "C"
.cpp 中的 C++ 弱函数
↓
.cpp 中重新定义时不需要 extern "C"