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"