Skip to content

Wiki

Function improvement

About 6 min read

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"