目录
[一、按"是否属于类"划分:成员 / 非成员](#一、按“是否属于类”划分:成员 / 非成员)
[1.1 成员函数(Member Function)](#1.1 成员函数(Member Function))
[(2)静态成员函数(Static Member Function)](#(2)静态成员函数(Static Member Function))
[1.2 非成员函数(Free Function)](#1.2 非成员函数(Free Function))
[1.3 友元函数(Friend Function)](#1.3 友元函数(Friend Function))
[2)示例:常见于 operator<<](#2)示例:常见于 operator<<)
[二、按"是不是特殊成员函数"划分(存在C++ 标准)](#二、按“是不是特殊成员函数”划分(存在C++ 标准))
[2.1 构造函数(Constructor)](#2.1 构造函数(Constructor))
[2.2 拷贝构造函数(Copy Constructor)](#2.2 拷贝构造函数(Copy Constructor))
[2.3 移动构造函数(Move Constructor)](#2.3 移动构造函数(Move Constructor))
[2.4 拷贝赋值运算符(Copy Assignment)](#2.4 拷贝赋值运算符(Copy Assignment))
[2.5 移动赋值运算符(Move Assignment)](#2.5 移动赋值运算符(Move Assignment))
[2.6 析构函数(Destructor)](#2.6 析构函数(Destructor))
[三、按"多态特性"划分:普通 / 虚函数 / 纯虚函数](#三、按“多态特性”划分:普通 / 虚函数 / 纯虚函数)
[3.1 普通成员函数](#3.1 普通成员函数)
[3.2 虚函数(virtual)](#3.2 虚函数(virtual))
[3.3 纯虚函数(pure virtual)](#3.3 纯虚函数(pure virtual))
[四、按"修饰符"划分:const / ref 限定 / noexcept / inline](#四、按“修饰符”划分:const / ref 限定 / noexcept / inline)
[4.1 const 成员函数](#4.1 const 成员函数)
[4.2 引用限定符成员函数(& / &&)](#4.2 引用限定符成员函数(& / &&))
[4.3 noexcept 函数](#4.3 noexcept 函数)
[4.4 inline 函数](#4.4 inline 函数)
[五、按"编译期特性"划分:模板 / constexpr / consteval](#五、按“编译期特性”划分:模板 / constexpr / consteval)
[5.1 函数模板(Function Template)](#5.1 函数模板(Function Template))
[5.2 constexpr 函数(可用于常量表达式)](#5.2 constexpr 函数(可用于常量表达式))
[5.3 consteval 函数(必须在编译期算)](#5.3 consteval 函数(必须在编译期算))
[六、可调用对象扩展:lambda / 函数对象 / 函数指针](#六、可调用对象扩展:lambda / 函数对象 / 函数指针)
[6.1 Lambda 表达式](#6.1 Lambda 表达式)
[6.2 函数对象(仿函数)](#6.2 函数对象(仿函数))
[6.3 函数指针 & 成员函数指针](#6.3 函数指针 & 成员函数指针)
[七、总表:C++ 函数"类型家族"一览](#七、总表:C++ 函数“类型家族”一览)
在之前的学习中,已经详细的介绍了成员函数、构造 / 析构函数、成员函数运算符重载、多态虚函数、C++变量作用域的相关内容。想要迅速了解,传送门:
C++ 内存机制详细全讲解:构造函数、析构函数、new/delete、栈 vs 堆 完整指南(小白教程)_结构体构造函数是堆内存还是栈内存-CSDN博客
https://blog.csdn.net/m0_58954356/article/details/155098091?spm=1001.2014.3001.550221. 图形的面积 ------ C++多态与虚函数实战讲解_经典的c++21 版项目-CSDN博客
https://blog.csdn.net/m0_58954356/article/details/154449284?ops_request_misc=%257B%2522request%255Fid%2522%253A%25228da7df0f814a6cb6c720203ffe8d202c%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=8da7df0f814a6cb6c720203ffe8d202c&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-8-154449284-null-null.nonecase&utm_term=%E5%87%BD%E6%95%B0&spm=1018.2226.3001.4450C++ 变量作用域详解(最全总结)_c++作用域-CSDN博客
https://blog.csdn.net/m0_58954356/article/details/154583518?ops_request_misc=%257B%2522request%255Fid%2522%253A%25228da7df0f814a6cb6c720203ffe8d202c%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=8da7df0f814a6cb6c720203ffe8d202c&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-18-154583518-null-null.nonecase&utm_term=%E5%87%BD%E6%95%B0&spm=1018.2226.3001.4450C++ 多态终极完整版:从虚函数到 vtable、对象切片、插件框架设计、面试题库全覆盖-CSDN博客
https://blog.csdn.net/m0_58954356/article/details/154891712?ops_request_misc=%257B%2522request%255Fid%2522%253A%25228da7df0f814a6cb6c720203ffe8d202c%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=8da7df0f814a6cb6c720203ffe8d202c&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-4-154891712-null-null.nonecase&utm_term=%E5%87%BD%E6%95%B0&spm=1018.2226.3001.4450C++ 成员函数运算符重载深度解析-CSDN博客
https://blog.csdn.net/m0_58954356/article/details/155754511?ops_request_misc=%257B%2522request%255Fid%2522%253A%25228da7df0f814a6cb6c720203ffe8d202c%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=8da7df0f814a6cb6c720203ffe8d202c&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-155754511-null-null.nonecase&utm_term=%E5%87%BD%E6%95%B0&spm=1018.2226.3001.4450
前言
C++ 中,"函数"远不止 void foo() 这么简单。
从最基础的 成员函数与非成员函数
到类内部的 构造 / 析构 / 拷贝 / 移动
到支持多态的 virtual / override / pure virtual
再到编译期机制的 模板函数 / constexpr / consteval
以及现代 C++ 常用的 lambda、函数对象、函数指针、成员函数指针......
这些构成了 C++ 中"可调用实体(Callable Entities)"的完整体系。
很多人在学习 C++ 时只了解:
-
成员函数
-
非成员函数 / 全局函数
-
静态成员函数
但实际上,这只是函数分类的"冰山一角"。
在面试和实际工程中,面试官常常会问:
C++ 函数到底分为哪些类型?
成员函数与非成员函数的本质区别是什么?
构造、析构、拷贝构造属于哪类函数?
虚函数、纯虚函数的机制是什么?
const 成员函数与 static 成员函数的区别?
lambda 与函数对象是否属于函数?
constexpr / consteval 函数有什么用?
为了帮助大家彻底厘清 C++ 函数体系结构,这篇文章将从多个角度(类归属、多态属性、特殊规则、编译期、可调用对象)系统梳理 C++ 中所有常见的函数类型,并配合示例代码、适用场景、对比表格,一站式掌握面试高频点。
一、按"是否属于类"划分:成员 / 非成员
1.1 成员函数(Member Function)
1)定义
-
定义在类内部,属于类。
-
调用时有一个隐藏参数
this,指向调用对象。 -
可以直接访问该类所有成员(包括 private / protected)。
2)分类
非静态成员函数(普通的成员函数)
静态成员函数 (
static修饰,不依赖对象)
(1)非静态成员函数
cpp
class Vec2 {
public:
double x, y;
void set(double nx, double ny) { // 非静态成员函数
x = nx; // 实际是 this->x = nx;
y = ny;
}
double length() const { // const 成员函数
return std::sqrt(x * x + y * y);
}
};
调用:
cpp
Vec2 v;
v.set(1, 2);
double len = v.length();
(2)静态成员函数(Static Member Function)
只能访问静态成员
cpp
class Config {
public:
static int version;
static int getVersion() { // 静态成员函数
return version; // 只能访问静态成员
}
};
int Config::version = 1;
int main() {
int v = Config::getVersion(); // 不需要对象
}
特点:
-
无
this指针。 -
不能访问非静态成员。
-
用于"类级别"的逻辑(计数器、工厂、单例等)。
1.2 非成员函数(Free Function)
1)定义
-
定义在类外部,不属于任何类。
-
没有
this指针。 -
不能直接访问类的 private 成员(除非做友元)。
-
又可以细分:
-
全局函数(在全局作用域)
-
命名空间内函数
-
静态自由函数
static(内部链接)
-
2)示例
(1)全局函数
cpp
int add(int a, int b) {
return a + b;
}
int main() {
int r = add(1, 2);
}
(2)命名空间内函数
cpp
namespace math {
int sub(int a, int b) {
return a - b;
}
}
(3)文件内静态函数(内部链接)
cpp
static void logInternal(const std::string& msg) {
// 只能在当前 .cpp 文件内可见
}
1.3 友元函数(Friend Function)
1)定义
语法上是非成员函数,但被类声明为****
friend。可以访问该类的 private / protected 成员。
2)示例:常见于 operator<<
cpp
class Vec2 {
private:
double x, y;
public:
Vec2(double x, double y) : x(x), y(y) {}
friend std::ostream& operator<<(std::ostream& os, const Vec2& v);
};
std::ostream& operator<<(std::ostream& os, const Vec2& v) {
return os << "(" << v.x << ", " << v.y << ")"; // 访问了 private
}
二、按"是不是特殊成员函数"划分(存在C++ 标准)
C++ 标准里有一组叫 "特殊成员函数"(special member function),编译器会自动生成或参与规则:
2.1 构造函数(Constructor)
1)默认构造函数
cpp
class A {
public:
A() { } // 默认构造
};
2)带参数构造函数
cpp
class A {
public:
A(int x, double y) { }
};
3)委托构造函数
cpp
class A {
public:
A() : A(0) {} // 委托给 A(int)
A(int x) { }
};
2.2 拷贝构造函数(Copy Constructor)
cpp
class A {
public:
A(const A& other) { // 拷贝构造
// 从 other 复制
}
};
在以下情况下调用:
-
A b = a; -
按值传参 / 返回值等场景。
2.3 移动构造函数(Move Constructor)
cpp
class A {
public:
A(A&& other) noexcept { // 移动构造
// 从 other"偷走"资源
}
};
在 A b = std::move(a); 时调用。
2.4 拷贝赋值运算符(Copy Assignment)
cpp
class A {
public:
A& operator=(const A& other) { // 拷贝赋值
if (this != &other) {
// 释放原资源 + 拷贝 new
}
return *this;
}
};
2.5 移动赋值运算符(Move Assignment)
cpp
class A {
public:
A& operator=(A&& other) noexcept { // 移动赋值
if (this != &other) {
// 释放原资源 + 偷走 other
}
return *this;
}
};
2.6 析构函数(Destructor)
cpp
class A {
public:
~A() { // 析构函数
// 释放资源
}
};
对象生命周期结束时自动调用。
三、按"多态特性"划分:普通 / 虚函数 / 纯虚函数
3.1 普通成员函数
没有 virtual 的正常成员函数。
cpp
class Base {
public:
void foo() { std::cout << "Base::foo\n"; }
};
3.2 虚函数(virtual)
支持运行时多态。
cpp
class Base {
public:
virtual void foo() {
std::cout << "Base::foo\n";
}
};
class Derived : public Base {
public:
void foo() override {
std::cout << "Derived::foo\n";
}
};
Base* p = new Derived;
p->foo(); // 调用 Derived::foo(虚函数动态绑定)
3.3 纯虚函数(pure virtual)
cpp
class Shape {
public:
virtual void draw() = 0; // 纯虚函数
};
特点:
-
含纯虚函数的类是抽象类,不能直接实例化。
-
必须在子类中重写才能使用。
四、按"修饰符"划分:const / ref 限定 / noexcept / inline
4.1 const 成员函数
cpp
class Vec2 {
public:
double x, y;
Vec2(double x = 0, double y = 0) : x(x), y(y) {}
// 重载+运算符:成员函数版本
Vec2 operator+(const Vec2& other) const {
// 自定义"加法规则":x加x,y加y
return Vec2(x + other.x, y + other.y);
}
};
含义:
-
函数体内不能修改成员(除 mutable)。
-
this是const Vec2*。 -
可以在
const Vec2对象上调用。
4.2 引用限定符成员函数(& / &&)
区分左值对象 / 右值对象调用。
左值 = 能取地址、有名字的对象
右值 = 临时对象、无名对象、即将被销毁的对象
cpp
class Str {
public:
std::string data;
std::string&& moveData() && { // 只能用于右值
return std::move(data);
}
std::string& getData() & { // 只能用于左值
return data;
}
};
Str s;
s.getData(); // OK,左值对象
// s.moveData(); // ❌ 错误,必须右值调用
Str().moveData(); // OK,临时右值对象
4.3 noexcept 函数
承诺不会抛异常,利于优化&异常安全。
cpp
void f() noexcept { // 不允许抛出异常 }
4.4 inline 函数
-
建议编译器内联(不保证)。
-
定义在类内部的成员函数默认是 inline。
cpp
inline int add(int a, int b) {
return a + b;
}
class A {
public:
void foo() { } // 默认 inline
};
五、按"编译期特性"划分:模板 / constexpr / consteval
5.1 函数模板(Function Template)
cpp
template<typename T>
T add(T a, T b) {
return a + b;
}
int x = add(1, 2); // 实例化 T=int
double y = add(1.5, 2.5); // 实例化 T=double
5.2 constexpr 函数(可用于常量表达式)
cpp
constexpr int sqr(int x) {
return x * x;
}
constexpr int v = sqr(5); // 编译期计算
int arr[sqr(3)]; // 数组大小也可以用
5.3 consteval 函数(必须在编译期算)
C++20 引入。
cpp
consteval int foo(int x) {
return x * 2;
}
constexpr int v = foo(10); // OK,编译期
int x = foo(10); // 也必须编译期就算好
六、可调用对象扩展:lambda / 函数对象 / 函数指针
严格说这些不都是"用关键字 function 定义的函数",
但在面试 & 工程中都算"函数类型家族"的一部分。
6.1 Lambda 表达式
cpp
auto add = [](int a, int b) {
return a + b;
};
int r = add(1, 2);
本质上:编译器生成了一个匿名类,重载 operator()。
6.2 函数对象(仿函数)
cpp
struct Add {
int operator()(int a, int b) const {
return a + b;
}
};
Add add;
int r = add(1, 2);
STL 里大量使用,如 std::less<>、std::hash<>。
6.3 函数指针 & 成员函数指针
cpp
// 普通函数指针
int add(int a, int b) { return a + b; }
int (*pf)(int, int) = &add;
// 成员函数指针
struct A {
void foo(int) { }
};
void (A::*pm)(int) = &A::foo;
A obj;
(obj.*pm)(42);
七、总表:C++ 函数"类型家族"一览
| 分类轴 | 类型 | 示例 | 说明 |
|---|---|---|---|
| 归属 | 非成员函数 | int add(int,int); |
不属于类,全局/命名空间 |
| 归属 | 成员函数 | void foo(); |
属于类,有 this |
| 归属 | 静态成员函数 | static void init(); |
无 this,只能访问静态成员 |
| 权限 | 友元函数 | friend std::ostream& operator<<(...) |
非成员,但可访问 private |
| 特殊成员 | 构造函数 | A(); A(int); |
创建对象 |
| 特殊成员 | 拷贝构造 | A(const A&); |
复制对象 |
| 特殊成员 | 移动构造 | A(A&&); |
移动资源 |
| 特殊成员 | 析构函数 | ~A(); |
销毁对象 |
| 特殊成员 | 拷贝赋值 | A& operator=(const A&); |
赋值拷贝 |
| 特殊成员 | 移动赋值 | A& operator=(A&&); |
移动赋值 |
| 多态 | 虚函数 | virtual void f(); |
支持运行时多态 |
| 多态 | 纯虚函数 | virtual void f() = 0; |
抽象接口 |
| 修饰 | const 成员函数 | int get() const; |
不修改对象 |
| 修饰 | ref 限定函数 | void f() &; |
区分左值/右值 this |
| 修饰 | noexcept | void f() noexcept; |
承诺不抛异常 |
| 修饰 | inline | inline int f(); |
建议内联 |
| 编译期 | 函数模板 | template<class T> T add(T,T); |
类型参数化 |
| 编译期 | constexpr 函数 | constexpr int f(int); |
可用于常量表达式 |
| 编译期 | consteval 函数 | consteval int f(int); |
必须编译期执行 |
| 可调用对象 | lambda | auto f = [](...){}; |
匿名仿函数 |
| 可调用对象 | 函数对象 | struct F { void operator()(); }; |
自定义调用行为 |
| 可调用对象 | 函数指针 | void (*pf)(); |
指向函数 |
| 可调用对象 | 成员函数指针 | void (C::*pm)(); |
指向成员函数 |
八、总结
从"C++ 八股"角度看,你可以说:
C++ 里的函数,大体可以从 3 条线理解:
归属:成员 / 静态成员 / 非成员 / 全局 / 友元
行为:普通 / const / static / virtual / 纯虚 / inline / noexcept 等
编译期:模板 / constexpr / consteval
再加上 lambda、仿函数、函数指针,就是基本完整的"可调用对象家族"。