一、函数核心作用
把一段重复、独立的逻辑封装起来,一次定义、多处调用,实现代码复用、逻辑分层、降低维护成本。
C++函数分为:普通全局函数、类成员函数(构造/析构/普通成员/静态成员/常函数)、内联函数、模板函数、Lambda匿名函数。
二、函数基础语法
1. 完整结构
// 返回值类型 函数名(参数列表) { 函数体; return 返回值; } int add(int a, int b) { return a + b; }1. 返回值类型:函数执行完返回的数据类型;无返回写 void
2. 函数名:标识符,见名知意,不能是关键字
3. 参数列表:0个/多个,逗号分隔;形参(定义处)、实参(调用处)
4. 函数体:大括号内执行逻辑
5. return:终止函数,带回返回值;void函数可省略 return
2. 无参、无返回示例
void printHello() { std::cout << "Hello C++" << std::endl; } // 调用 printHello();3. 有参有返回
double mul(double x, double y) { return x * y; } double res = mul(3.14, 2);三、函数声明 vs 函数定义(对应.h/.cpp)
1. 声明(只告诉编译器函数长什么样,无实现)
放在头文件 .h,作用:提前告知接口,解决“调用在定义前”报错。语法:末尾加分号。
// 声明 int add(int a, int b);2. 定义(完整实现逻辑)
放在 .cpp,只能定义一次。
int add(int a, int b) { return a + b; }工程规范:
.h:放函数声明
.cpp:#include 对应头文件,写函数实现
四、函数参数四大传递方式
1. 值传递(拷贝,最常用)
实参拷贝一份给形参,函数内修改形参不影响外部变量。
void test(int x) { x = 100; // 只改副本 } int a = 10; test(a); // a 依旧是 10缺点:大对象(string、类)拷贝开销大。
2. 引用传递 & 常引用(&/const&)
(1)普通引用 T&
形参是实参别名,修改形参直接改外部变量,无拷贝开销。
void modify(int& x) { x = 999; } int num = 10; modify(num); // num = 999(2)const 常引用 const T&(最优传参)
只读取、不修改,无拷贝、不能改原数据,传递字符串/类首选。
void showStr(const std::string& s) { std::cout << s; // s = "xxx"; // 报错,const 不可修改 }3. 指针传递 T*
传递变量地址,通过解引用修改原值,兼容C语言。
void setVal(int* p) { *p = 666; } int n = 1; setVal(&n);4. 数组参数
数组传参等价于指针,会丢失长度,需额外传大小。
void printArr(int arr[], int len) { for (int i = 0; i < len; i++) std::cout << arr[i]; }五、默认参数
定义函数时给参数预设值,调用不传则使用默认值。规则:带默认值的参数必须靠右。
// 正确:默认参数放后面 void func(int a, int b = 10, double c = 3.14) {} // 调用多种写法 func(1); // b=10, c=3.14 func(1, 20); // c=3.14 func(1, 20, 9.9); // 错误:默认参数不能靠左 // void func(int a = 10, int b) // 编译报错注意:声明和定义只能一处写默认值,推荐写在 .h 声明里。
六、函数重载 Overload
同一作用域,函数名相同,参数列表不同(个数/类型/顺序),编译器自动匹配对应函数。返回值不能作为重载区分依据。
// 重载1 int calc(int a) { return a; } // 重载2 int calc(int a, int b) { return a + b; } // 重载3 double calc(double a) { return a * 2; } calc(10); // 调用重载1 calc(1, 2); // 调用重载2 calc(2.5); // 调用重载3Qt 中大量使用重载(按钮点击、控件构造)。
七、特殊函数:类内成员函数
1. 构造函数 Constructor
函数名=类名,无返回值,可重载。创建对象自动调用,用于初始化成员变量。
class Person { public: Person() { // 无参构造 age = 0; } Person(int a) { // 有参构造(重载) age = a; } private: int age; }; Person p1; // 调用无参构造 Person p2(18); // 调用有参构造2. 析构函数 Destructor ~类名()
如:~MainWindow()
波浪号~+类名,无参数、无返回、只能一个。对象销毁时自动调用,释放堆内存、资源、句柄。
class MainWindow { public: MainWindow(); ~MainWindow(); // 析构 };3. 常成员函数 const
函数末尾加 const,函数内不能修改成员变量,只能读取。
class Test { public: int getNum() const { // num = 10; // 报错 return num; } private: int num = 5; };4. 静态成员函数 static
属于类,不属于单个对象,只能访问静态成员,没有 this 指针。
class Math { public: static int max(int a, int b) { return a > b ? a : b; } }; // 直接类名调用,不用创建对象 Math::max(3, 9);八、内联函数 inline
普通函数调用有压栈跳转开销,inline 提示编译器将函数代码直接嵌入调用处,省去调用开销。适用:代码很短(1~5行)、频繁调用的函数。
inline int square(int x) { return x * x; }模板函数默认隐式 inline,必须写在头文件。
九、模板函数 template
通用函数,适配任意数据类型,解决重复写 int/double/string 同款逻辑。
template<typename T> T add(T a, T b) { return a + b; } add<int>(1, 2); // 返回 3 add<double>(1.1, 2.2); // 返回 3.3强制放 .h 头文件,编译器需要完整定义才能实例化。
十、Lambda 匿名函数 C++11+
不用定义函数名,临时写一段逻辑,常用于 Qt 信号槽、排序、遍历。
auto func = [](int x) -> int { return x * 2; }; std::cout << func(5); // 输出 10捕获列表 []:[=] 值捕获、[&] 引用捕获外部变量。
十一、函数递归
函数内部自己调用自己,必须有终止条件,否则死循环栈溢出。
// 阶乘 int fact(int n) { if (n <= 1) return 1; // 终止条件 return n * fact(n - 1); }十二、返回值补充
1. void:无返回,可写 return; 提前退出
2. 引用返回 int&:可以作为左值修改原变量
3. 指针返回 int*:返回堆地址,记得外部释放
4. 函数不能返回局部普通变量的引用/指针(局部变量销毁,悬空引用)
十三、常见易错点
1. 函数声明加分号,定义不能加分号
2. 模板、inline 放头文件;普通实现放 .cpp
3. 重载只看参数,返回值不算区分条件
4. 默认参数从右往左写
5. 传大型对象优先 const&,减少拷贝
6. 析构函数不要手动调用,对象销毁自动执行