C++学习笔记——函数指针、Lambda表达式、谨慎使用using namespace std、命名空间

目录

一、函数指针

二、Lambda表达式

2.1 基础语法

2.2  Lambda 与 std::function / 函数指针

三、谨慎使用using namespace std

1. 命名空间污染

 2. 破坏代码的明确性与可读性

3. 头文件中使用是灾难

四、命名空间

4.1 为什么需要命名空间?

4.2 定义与使用命名空间

4.3 using 的三种用法


一、函数指针

简单来说,函数指针就是指向函数的指针变量,它允许你将函数作为参数传递,从而实现更灵活、动态的程序行为。这种技术在实现回调函数(Callback) 时尤其有用。

概念说明
本质函数存储在内存中,因此有地址。函数指针就是存储这个地址的变量
获取地址直接使用函数名(如 HelloWorld)而不加括号 (),即可获得函数地址。&HelloWorld 也是有效的,但函数名本身会隐式转换。
主要用途1. 间接调用函数
2. 作为参数传递,实现回调
 
现代实践现代 C++ 中常用 std::function 和 Lambda 表达式来替代。
#include <iostream>
#include <vector>

// 一个接收函数指针作为参数的函数
void ForEach(const std::vector<int>& values, void(*func)(int)) {
    for (int value : values) {
        func(value); // 通过函数指针调用传入的函数
    }
}

// 一个具体的操作函数
void PrintValue(int value) {
    std::cout << "Value: " << value << std::endl;
}

void HelloWorld() {
    std::cout << "Hello World!" << std::endl;
}

int Add(int a, int b) {
    return a + b;
}

// 使用 typedef
typedef void(*HelloWorldFunction)();

// 使用 using (现代C++推荐)
using AddFunction = int(*)(int, int);

int main() {
    HelloWorldFunction func1 = HelloWorld;
    func1();

    AddFunction func2 = Add;
    int result = func2(3, 5); // result 为 8

    std::vector<int> values = {1, 2, 3, 4, 5};
    // 将 PrintValue 函数作为参数传递给 ForEach
    ForEach(values, PrintValue);
    return 0;
}

二、Lambda表达式

2.1 基础语法

一个 Lambda 表达式通常写成如下形式:

[capture](parameters) -> return_type { body }
组成部分说明
[capture]捕获列表:指定哪些外部变量可以被 Lambda 内部访问。
(parameters)参数列表:与普通函数类似,可以省略(无参时括号可略)。
-> return_type返回类型:通常可省略,编译器会从 return 语句推导。
{ body }函数体:具体逻辑代码。

捕获列表决定了 Lambda 能否使用其定义所在作用域中的变量。这是 Lambda 与普通函数指针最大的不同点。

捕获方式写法说明
空捕获[]不使用任何外部变量。
值捕获[=]复制方式捕获所有外部变量(Lambda 内部不能修改副本,除非加 mutable)。
引用捕获[&]引用方式捕获所有外部变量(可以修改原变量)。
显式值捕获[x, y]只复制 x 和 y
显式引用捕获[&x, &y]只引用 x 和 y
混合捕获[=, &x]除 x 用引用外,其余变量用值捕获。
[&, x]x 用值捕获,其余用引用捕获。
std::vector<int> values = {1, 2, 3, 4, 5};

// 之前的写法:传递函数指针
void Print(int v) { std::cout << v << " "; }
ForEach(values, Print);

// Lambda 写法:内联定义
ForEach(values, [](int v) { std::cout << v << " "; });

2.2  Lambda 与 std::function / 函数指针

特性Lambda (无捕获)Lambda (有捕获)std::function
能否转成函数指针✅ 可以(隐式转换)❌ 不可以❌ 不可以
存储开销极小(同函数指针)捕获变量大小有一定开销(类型擦除)
灵活性只能使用全局/静态信息可使用任意上下文变量可包装任何可调用对象

示例:无捕获的 Lambda 可以赋值给函数指针

void (*funcPtr)(int) = [](int x) { std::cout << x; }; // 合法

而有捕获的 Lambda 则不行:

int y = 5;
// void (*badPtr)(int) = [y](int x) { std::cout << x + y; }; // 编译错误

此时需要用 std::function 接收 有捕获的Lambda表达式:

#include <functional>
std::function<void(int)> func = [y](int x) { std::cout << x + y; };
func(10); // 输出 15

三、谨慎使用using namespace std

1. 命名空间污染

using namespace std; 会将 std 命名空间中的所有名称(如 vectorstringsortfind 等)引入当前作用域。一旦你自定义了同名函数或类(例如自己写的 vector 或 sort),就会发生名称冲突,导致编译错误或更隐蔽的意外重载

#include <iostream>
using namespace std;

int vector = 42;   // 错误:std::vector 也是名称,冲突!

 2. 破坏代码的明确性与可读性

使用 std:: 前缀可以让代码的读者一眼知道某个名字来自标准库。省略前缀后,读到 vectorsortcopy 时,无法区分是标准库还是自定义的,增加理解成本。

3. 头文件中使用是灾难

如果在一个头文件中使用 using namespace std;,那么所有包含这个头文件的 .cpp 文件都会被“污染”– 被迫引入整个 std 命名空间。这会导致不可控的名称冲突,且难以追踪。永远不要在头文件中使用 using namespace std;

四、命名空间

命名空间是 C++ 中用于组织代码、防止名称冲突的核心机制。

4.1 为什么需要命名空间?

其作用包括:

  • 避免全局命名污染:大型项目中,多个开发者可能无意中使用相同的全局名称。

  • 逻辑分组:将相关功能(如数学计算、文件 I/O)放入同一命名空间,提高可读性。

  • 控制符号可见性:配合 using 指令灵活管理哪些名称进入当前作用域。

例如,没有命名空间时,两个库都定义了 print() 函数:

// 库 A
void print(const std::string& s) { ... }

// 库 B
void print(int x) { ... }

// 你的代码:调用哪个 print?编译器无法区分。

有了命名空间,可以明确区分。

4.2 定义与使用命名空间

基本定义

namespace MyNamespace {
    int value = 10;
    void func() { std::cout << "func\n"; }
    
    class MyClass { ... };
}

访问成员:作用域运算符 ::

MyNamespace::value = 20;
MyNamespace::func();
MyNamespace::MyClass obj;

嵌套命名空间

namespace Outer {
    namespace Inner {
        void nested() { ... }
    }
}
// 调用
Outer::Inner::nested();

4.3 using 的三种用法

形式作用域影响安全性
using namespace X;将 X 中所有名称引入当前作用域高风险(名称污染)
using X::symbol;仅引入单个符号低风险
namespace alias = ...;为命名空间起别名无风险

示例:

namespace LongNameSpace { int x; }

// 别名
namespace Short = LongNameSpace;

// 引入单个符号
using std::cout;
using std::endl;

// 引入整个命名空间(谨慎!)
using namespace std;   // 不推荐

内联命名空间

内联命名空间中的符号会自动被提升到外层命名空间,常用于版本管理

namespace MyLib {
    inline namespace v1 {
        void feature() { std::cout << "v1\n"; }
    }
    namespace v2 {
        void feature() { std::cout << "v2\n"; }
    }
}

int main() {
    MyLib::feature();   // 调用 v1 版本(因为 v1 是内联的)
    MyLib::v2::feature(); // 显式调用 v2
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值