当前位置: 首页 > article >正文

C++:函数(通识版)

一、函数的基础

1.什么是函数?(独立的功能单位)

函数是C++中封装代码逻辑的基本单元,用于执行特定任务。
作用:代码复用、模块化、提高可读性。


2、函数的基本结构

返回类型 函数名(参数列表) {// 函数体return 返回值; // 若返回类型非void
}
示例1:最简单的函数
#include<iostream>
using namespace std;
int func(int n)
{
/*求解阶乘
输入:一个正整数n
输出:n的阶乘*/int result = 1;while(n > 1){result *= n--;}return result;
}int main() {int r=func(5);//调用cout<<r<<endl;return 0;
}
调用过程 如下:

函数的调用完成两项工作:一是用实参初始化函数对应的形参,二是将控制权转移给被调用函数。此时,主调函数(calling function)的执行被暂时中断,被调函数(called function)开始执行。

当遇到一条 return 语句时函数结束执行过程。和函数调用一样,return 语句也完成两项工作:一是返回 return 语句中的值(如果有的话),二是将控制权从被调函数转移回主调函数。函数的返回值用于初始化调用表达式的结果,之后继续完成调用所在的表达式的剩余部分。 

func等效如下:
int n = 5; // 用字面值 5 初始化 n
int result = 1; //func 函数体内的代码
while (n> 1)
result *= n--;
int r = result; // 用 result的副本初始化 j

 3.局部对象

 什么是局部对象?

局部对象是指在函数内部或代码块(如{}包围的语句块)内定义的对象。
特点

  • 作用域(Scope)​:仅在定义它的函数或代码块内可见。
  • 生命周期(Lifetime)​:从定义处开始,到离开作用域时自动销毁。

局部对象的生命周期
1. 普通局部对象(自动对象)
  • 创建:执行到定义语句时创建。
  • 销毁:离开作用域时,按定义顺序的逆序自动销毁。
  • 存储位置:通常存储在栈(Stack)内存中,由编译器自动管理。
#include<iostream>
using namespace std;
void func(int n)
{int a = n;           // 局部对象a,作用域在func内{                    // 进入代码块double b = 3.14; // 局部对象b,作用域仅在此代码块内cout << a << " " << b << endl; // 输出a和b}// 离开代码块,b被销毁// a仍有效                    cout << a << endl;
}// 离开函数,a被销毁int main() {func(10);return 0;
}

 生命周期可视化如下:

2. 局部静态对象
  • static关键字修饰的局部对象。
  • 生命周期:从首次执行定义语句开始,到程序结束。
  • 初始化:仅第一次执行时初始化一次。

如果有必要令局部变量的生命周期贯穿函数调用及之后的时间,可以将局部变量定义成 static 类型从而获得这样的对象。局部静态对象(local static object)在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。

void counter() {static int count = 0; // 静态局部对象,生命周期为整个程序count++;cout << count << endl;
}int main() {counter(); // 输出1counter(); // 输出2return 0;
}

 


3、局部对象的作用域限制

局部对象仅在定义的作用域内可见,外部无法访问。

void func1() {int x = 5; // 局部对象x
}void func2() {// cout << x; // 错误!x在func1中,此处不可见
}

4、局部对象与全局对象的对比
特性局部对象全局对象
作用域定义所在的函数或代码块整个程序
生命周期作用域内有效程序运行期间始终有效
存储位置栈内存(自动管理)全局数据区(静态存储)
访问权限仅在作用域内可访问所有函数可访问
初始化每次进入作用域时初始化程序启动时初始化

5、注意事项
1. 不要返回局部对象的指针或引用

局部对象在函数返回后会被销毁,返回其指针或引用会导致悬垂指针/引用​(Dangling Pointer/Reference)。

// 错误示例:返回局部对象的引用
int& badFunction() {int x = 10; // x是局部对象,函数返回后x被销毁return x;    // 返回悬垂引用,未定义行为!
}// 正确做法:返回值而非引用
int goodFunction() {int x = 10;return x; // 返回x的副本
}
2. 对象销毁顺序

局部对象按定义的逆序销毁,依赖其他对象的资源时需注意顺序。

class Logger {
public:Logger() { cout << "Logger created" << endl; }~Logger() { cout << "Logger destroyed" << endl; }
};void test() {Logger log1;  // 先创建Logger log2;  // 后创建
} // 先销毁log2,再销毁log1(逆序销毁)
3. 避免在局部作用域中创建过大对象

栈内存有限(通常几MB),过大的对象可能导致栈溢出。
解决方案:使用动态内存(堆内存)分配。

void safeFunction() {// 栈内存可能溢出(错误)// int hugeArray[1000000]; // 改用堆内存(正确)int* hugeArray = new int[1000000];delete[] hugeArray; // 需手动释放
}

6、实际应用示例
示例1:局部对象在循环中的使用
#include <iostream>
using namespace std;int main() {for (int i = 0; i < 5; i++) { // i是局部对象,仅在循环内有效string message = "Iteration: " + to_string(i); // message每次循环重新创建cout << message << endl;} // 每次循环结束,message和i的当前副本被销毁return 0;
}
示例2:利用RAII管理局部资源
#include <fstream>
using namespace std;void readFile() {ifstream file("data.txt"); // 局部对象file,打开文件if (file.is_open()) {// 读取文件内容string line;while (getline(file, line)) {cout << line << endl;}} // 离开作用域时,file的析构函数自动关闭文件
}

readFile 函数展示了 C++ 中文件操作的基本用法,以及局部对象的生命周期管理。

1. 局部对象 file 的创建

ifstream file("data.txt"); // 局部对象file,打开文件

  • 使用 ifstream 类型创建了一个名为 file 的局部对象,用于读取文件 "data.txt"

  • 文件在构造函数中被打开。如果文件不存在或无法打开,file.is_open() 将返回 false

2. 检查文件是否成功打开

if (file.is_open()) { // 读取文件内容 }

  • 调用 file.is_open() 检查文件是否成功打开。

  • 如果文件未成功打开,程序将跳过文件读取部分。

3. 读取文件内容

string line; while (getline(file, line)) { cout << line << endl; }

  • 使用 getline 函数逐行读取文件内容。

  • 每次读取一行并存储到字符串变量 line 中。

  • 如果文件结束或发生错误,getline 返回 false,循环结束。

4. 局部对象的作用域和析构

} // 离开作用域时,file的析构函数自动关闭文件

  • 当程序执行到 } 时,局部对象 file 的作用域结束。

  • 在 C++ 中,当局部对象超出作用域时,其析构函数会被自动调用。

  • 对于 ifstream 对象,析构函数会自动关闭文件,因此无需手动调用 file.close()


总结

这段代码展示了 C++ 中文件读取的基本流程,以及局部对象的生命周期管理:

  1. 使用 ifstream 打开文件。

  2. 检查文件是否成功打开。

  3. 使用 getline 逐行读取文件内容。

  4. 离开作用域时,局部对象的析构函数自动释放资源(如关闭文件)。

这种设计利用了 C++ 的 RAII(Resource Acquisition Is Initialization)机制,确保资源(如文件句柄)在不再需要时能够被正确释放,避免资源泄漏。

小贴士:

C++ 的 RAII(Resource Acquisition Is Initialization,资源获取即初始化) 是一种管理资源的编程机制,核心思想是:通过对象的生命周期来控制资源的获取与释放

实现方式

  • 构造函数获取资源:对象创建时,在构造函数中申请资源(如内存、文件句柄、网络连接、互斥锁等)。

  • 析构函数释放资源:对象生命周期结束(如离开作用域、被销毁)时,自动调用析构函数释放资源。

核心优势

  • 避免资源泄漏:无论程序正常退出还是因异常终止,对象析构函数都会执行,确保资源释放。

  • 异常安全:若构造函数成功获取资源,后续操作即使抛出异常,析构函数仍会释放资源,保证程序状态正确。


7、小结
  1. 局部对象的作用域和生命周期是C++高效内存管理的基础。
  2. 避免返回局部对象的指针或引用,防止未定义行为。
  3. 合理使用静态局部对象实现跨函数调用的状态保持。
  4. 栈内存限制要求对大型对象使用动态内存分配。


4、函数组成要素详解

1. 返回类型
  • void表示无返回值。
  • 必须与return语句的类型匹配。
void printHello() {std::cout << "Hello!";// 无return语句(允许)
}
2. 参数列表
  • 可以是零个或多个参数,用逗号分隔。
  • 参数传递方式:值传递、引用传递、指针传递(后文详解)。
3. 函数体
  • 包含具体执行的代码。
  • 局部变量在函数结束时销毁。

 5、函数声明与定义

1、函数声明的作用

函数声明(也称函数原型,Function Prototype)的主要目的是向编译器告知函数的存在,允许在函数定义之前调用它。
核心作用

  1. 类型检查:确保调用时参数类型和返回值类型正确。

  2. 分离编译:支持多文件编程,声明通常放在头文件(.h)中。

  3. 解决依赖:允许函数A调用函数B,而无需考虑定义的先后顺序。


2、函数声明的语法
返回类型 函数名(参数类型列表);  // 结尾必须有分号!
  • 参数类型列表:只需指定参数类型,形参名可省略(但建议保留以增强可读性)。

  • 示例

    // 声明一个加法函数
    int add(int, int);          // 省略形参名
    int multiply(int a, int b); // 保留形参名(推荐)

3、函数声明 vs. 函数定义

特性

函数声明

函数定义

分号

必须有分号结尾

无分号

函数体

无函数体(仅声明接口)

必须包含函数体(具体实现)

出现次数

可多次声明(需一致)

只能定义一次(单定义规则)

示例1:声明与定义分离

// 声明(头文件 math_utils.h)
int add(int a, int b);// 定义(源文件 math_utils.cpp)
int add(int a, int b) {return a + b;
}

4、函数声明的位置

1. 在头文件中声明(推荐)

  • 头文件(.h)用于集中放置函数声明,供多个源文件共享。

  • 示例

    // math_utils.h
    #ifndef MATH_UTILS_H // 头文件保护,防止重复包含
    #define MATH_UTILS_Hint add(int a, int b);
    double add(double a, double b); // 函数重载声明#endif

2. 在调用前声明

  • 在调用函数的代码前直接声明(适用于简单程序)。

  • 示例

    #include <iostream>
    using namespace std;// 声明printMessage函数
    void printMessage(const string& message);int main() {printMessage("Hello, World!");return 0;
    }// 定义printMessage
    void printMessage(const string& message) {cout << message << endl;
    }

5、函数声明的注意事项

1. 默认参数的声明规则

  • 默认参数只能在函数声明中指定,不能在定义中重复。

  • 若声明和定义分离,默认参数应放在声明中(通常位于头文件)。

    // 声明(math_utils.h)
    void log(const string& message, int level = 1);// 定义(math_utils.cpp)
    void log(const string& message, int level) { // 此处不可写=1// 实现...
    }

2. 函数重载的声明

  • 重载函数的声明必须通过参数列表区分(返回类型不同不算重载)。

    // 合法重载
    int max(int a, int b);
    double max(double a, double b);// 错误:仅返回类型不同,无法重载
    double getValue();
    int getValue(); // 编译错误

3. 函数声明的作用域

  • 声明的作用域从声明位置开始,到所在作用域结束。

  • 示例

    void outer() {// 错误:inner()未声明// inner(); void inner(); // 声明inner函数inner();      // 合法调用
    }void inner() {cout << "Inner function" << endl;
    }

6、未声明函数的后果

若调用函数前未声明,可能导致:

  1. 编译错误:编译器无法识别函数。

  2. 隐式声明风险:C语言允许隐式声明,但C++已废弃此行为,现代编译器会报错。

    int main() {int result = add(3, 5); // 若未声明add,编译失败return 0;
    }

7、实际应用示例

示例1:多文件编程

// ---------- math_utils.h ----------
#ifndef MATH_UTILS_H
#define MATH_UTILS_Hint factorial(int n); // 声明阶乘函数#endif// ---------- math_utils.cpp ----------
#include "math_utils.h"int factorial(int n) { // 定义if (n <= 1) return 1;return n * factorial(n - 1);
}// ---------- main.cpp ----------
#include <iostream>
#include "math_utils.h"int main() {std::cout << factorial(5); // 输出120return 0;
}

示例2:前置声明解决循环依赖

// 前置声明B类
class B;class A {
public:void callB(B& b); // 声明函数,参数为B的引用
};class B {
public:void callA(A& a) { a.callB(*this); } // 定义函数
};// A::callB的定义需在B类定义之后
void A::callB(B& b) { /* ... */ }

8、小结

  1. 声明先于调用:函数必须在使用前声明。

  2. 头文件集中管理:声明应放在头文件中,并通过头文件保护避免重复包含。

  3. 默认参数在声明中指定:确保调用者可见。

  4. 声明与定义一致:参数列表和返回类型必须严格匹配。


二、参数传递

1、参数传递的基本概念

参数传递是函数调用时,将数据从调用方传递给函数的方式。
核心作用

  1. 控制函数内对原始数据的访问权限(是否允许修改原数据)。
  2. 优化性能(避免不必要的拷贝)。

2、值传递(Pass by Value)
1. 机制
  • 将实参的副本传递给函数,函数内修改形参不会影响原始数据。

  • 适用场景:基本数据类型(intdouble等)或小型结构体。

2. 示例
void increment(int x) {x++; // 修改的是副本
}int main() {int a = 5;increment(a);cout << a; // 输出5(原值未变)
}

3. 特点
  • 优点:安全,函数内操作不影响原数据。

  • 缺点:对大型对象(如类、数组)会产生拷贝开销。


3、引用传递(Pass by Reference)
1. 机制
  • 传递实参的别名​(引用),函数内修改形参直接影响原始数据。
  • 适用场景:需要修改原数据,或传递大型对象避免拷贝。
2. 示例
void increment(int &x) {x++; // 修改原数据
}int main() {int a = 5;increment(a);cout << a; // 输出6(原值被修改)
}

3. 特点
  • 优点:避免拷贝开销,允许修改原数据。
  • 缺点:需注意函数可能意外修改数据(可用const解决)。

4、指针传递(Pass by Pointer)
1. 机制
  • 传递实参的内存地址,函数内通过指针操作原始数据。
  • 适用场景:需要显式传递地址,或允许nullptr(空指针)的情况。
2. 示例
void increment(int *x) {if (x != nullptr) { // 必须检查空指针!(*x)++;         // 解引用后修改原数据}
}int main() {int a = 5;increment(&a);      // 传递地址cout << a;          // 输出6
}

3. 特点
  • 优点:明确表示可能修改原数据,支持动态内存操作。
  • 缺点:语法复杂,需手动检查空指针。

5、const修饰符的应用
1. const引用
  • 禁止通过引用修改原数据,同时避免拷贝。
void printLargeObject(const BigClass &obj) {// 只能读取obj,无法修改// 避免拷贝BigClass的副本
}
2. const指针
  • 禁止通过指针修改数据,或禁止修改指针本身。
void readData(const int *ptr) { // 不能通过ptr修改数据cout << *ptr;
}void readDate(int* const ptr){//不能修改ptr本身cout<<*ptr;
}

6、值传递 vs. 引用传递 vs. 指针传递对比
特性值传递引用传递指针传递
修改原数据不允许允许允许
拷贝开销有(副本)无(别名)无(传递地址)
空值支持不适用不适用支持(nullptr
语法简洁性简单简单较复杂
典型用途基本数据类型大型对象/需修改数据动态内存/可选参数

7、高级传递方式(C++11+)
1. 移动语义(Move Semantics)
  • 通过std::move将资源所有权转移,避免拷贝(适用于临时对象)。
void processBigData(BigData &&data) { // 右值引用// 移动资源,避免拷贝
}BigData data;
processBigData(std::move(data)); // 转移所有权
2. 万能引用(Universal Reference)与完美转发
  • 使用auto&&std::forward保留参数类型(模板编程中常见)。
template<typename T>
void relay(T &&arg) {process(std::forward<T>(arg)); // 完美转发
}

8、参数传递的选择指南
  1. 需要修改原数据 → 使用引用传递​(优先)或指针传递。
  2. 仅读取数据且对象较大 → 使用const引用传递。
  3. 基本数据类型(如int)​ → 值传递或const引用均可。
  4. 动态内存或可选参数 → 指针传递(需检查nullptr)。
  5. 避免拷贝临时对象 → 使用移动语义(C++11+)。

9、常见错误与解决方案
1. 返回局部对象的引用或指针
int& badFunction() {int x = 10;return x; // 错误:x在函数结束时被销毁
}

解决:返回值(副本)或传递动态分配的对象。

2. 悬垂引用(Dangling Reference)
int& ref;
{int a = 5;ref = a; // a的引用在代码块外失效
}
cout << ref; // 未定义行为

解决:确保引用的对象生命周期足够长(可以尝试static)。

3. 未初始化指针
int *ptr;
*ptr = 5; // ptr未初始化,指向无效地址

解决:始终初始化指针(如int *ptr = &valid_varptr = new int)。


10、一个小栗子
#include <iostream>
using namespace std;// 值传递:安全但低效(适合小对象)
void printValue(int val) {cout << "Value: " << val << endl;
}// 引用传递:高效且可修改原数据
void square(int &num) {num *= num;
}// const引用:高效且防止修改(适合大对象)
void printBigObject(const BigData &data) {data.display();
}// 指针传递:允许空值,显式传递地址
void allocateMemory(int ​**ptr) {*ptr = new int(100); // 修改指针指向的内容
}int main() {int a = 5;square(a);            // 引用传递,a变为25printValue(a);        // 值传递,输出25int *p = nullptr;allocateMemory(&p);   // 指针传递,p指向新内存cout << *p;           // 输出100delete p;return 0;
}

11、小结
  1. 值传递:简单安全,适合小型数据。
  2. 引用传递:高效灵活,优先用于大型对象或需修改原数据。
  3. 指针传递:灵活但需谨慎,适合动态内存或可选参数。
  4. const修饰符:保护数据不被意外修改,提升代码健壮性。
  5. 现代C++特性:移动语义和完美转发可优化资源管理。

三、返回类型和return语句

1、返回类型的作用

返回类型定义了函数返回数据的类型,决定了调用者如何接收和处理结果。
关键点

  • 必须与return语句返回的值类型匹配(可隐式转换)。
  • 若返回类型为void,函数不能返回任何值。

2、基本返回类型与return用法
1. 返回基本数据类型(intdouble等)
int add(int a, int b) {return a + b; // 返回int类型值
}double divide(double a, double b) {return a / b; // 返回double类型值
}
2. 返回void(无返回值)
void printHello() {cout << "Hello";return; // 可省略,函数执行到末尾自动返回
}void earlyExit(bool flag) {if (flag) {return; // 提前退出函数}cout << "Continue...";
}

3、返回引用与指针
1. 返回引用(避免拷贝,但需注意生命周期)
// 返回静态局部变量的引用(安全)
int& getStaticValue() {static int value = 10; // 静态变量,生命周期到程序结束return value;
}// 错误示例:返回局部变量的引用(悬垂引用)
int& badFunction() {int x = 5; // x是局部变量,函数返回后被销毁return x;   // 未定义行为!
}
2. 返回指针(需确保内存有效)
// 返回动态内存的指针(调用者需负责释放)
int* createArray(int size) {int* arr = new int[size];return arr;
}// 错误示例:返回局部变量的指针
int* badPointer() {int x = 10;return &x; // x被销毁后指针失效!
}

4、返回对象
1. 返回值对象(触发拷贝构造或移动构造)
class MyClass {
public:MyClass() { cout << "Constructor" << endl; }MyClass(const MyClass&) { cout << "Copy Constructor" << endl; }MyClass(MyClass&&) { cout << "Move Constructor" << endl; }
};MyClass createObject() {MyClass obj;return obj; // 可能触发返回值优化(RVO)
}int main() {MyClass obj = createObject(); // 输出可能仅为"Constructor"(RVO优化)
}
2. 使用移动语义提升效率(C++11+)
MyClass createObject() {MyClass obj;return std::move(obj); // 强制移动语义(可能阻止RVO)
}

5、返回类型与return的匹配规则
1. 隐式类型转换
  • return的值类型可隐式转换为返回类型。
double func() {return 3; // int隐式转换为double
}
2. 列表初始化(C++11+)
std::vector<int> getNumbers() {return {1, 2, 3}; // 返回初始化列表
}
3. 返回auto(C++14+)
auto add(double a, double b) -> decltype(a + b) {return a + b; // 返回类型由a+b推导
}// 更简化的写法(C++14)
auto multiply(double a, double b) {return a * b; // 自动推导返回类型
}

6、常见错误与解决方案
1. 返回局部对象的引用/指针
int& badRef() {int x = 10;return x; // x被销毁,返回悬垂引用
}

解决:返回静态变量、动态内存或参数中的引用。

2. 返回类型与return不匹配
int func() {return 3.14; // double转int导致精度丢失(警告)
}

解决:显式转换或修改返回类型。

3. 遗漏return语句
int badFunc(bool flag) {if (flag) {return 1;}// 未处理flag=false的情况,导致未定义行为
}

解决:确保所有路径都有返回值。


7、高级特性
1. 尾置返回类型(C++11+)
auto getData() -> int (*)[5] { // 返回指向int[5]的指针static int arr[5] = {1,2,3,4,5};return &arr;
}
2. 多返回值(通过结构体或std::tuple
#include <tuple>std::tuple<int, double> getValues() {return std::make_tuple(42, 3.14);
}int main() {auto [a, b] = getValues(); // C++17结构化绑定
}

8、小结
  1. 返回类型需与return值匹配,注意隐式转换规则。
  2. 返回引用/指针时,必须确保对象生命周期足够长。
  3. 返回对象时,优先依赖编译器优化(RVO),而非显式std::move
  4. ​现代C++特性(如auto、移动语义、结构化绑定)可简化代码并提升效率。
  5. 避免常见错误:悬垂引用、遗漏return、类型不匹配。

四、函数重载(Overloading)

1、什么是函数重载?

函数重载允许在同一作用域内定义多个同名函数,但这些函数的参数列表必须不同​(参数类型、数量或顺序不同)。
核心目的提供语义相同的操作,但支持不同类型或数量的参数,提升代码可读性和灵活性。

// 重载示例
int sum(int a, int b) { return a + b; }
double sum(double a, double b) { return a + b; }
int sum(int a, int b, int c) { return a + b + c; }

2、函数重载的规则
  1. 参数列表必须不同:以下任一条件满足即可:
    • 参数类型不同(如int vs double)。
    • 参数数量不同。
    • 参数顺序不同(但需类型不同)。
  2. 返回类型不同不构成重载:仅返回类型不同会导致编译错误。
  3. 作用域相同:重载函数必须在同一作用域(如全局作用域或同一类中)。

3、函数重载的示例
示例1:参数类型不同
void print(int x) {cout << "Integer: " << x << endl;
}void print(double x) {cout << "Double: " << x << endl;
}int main() {print(5);    // 调用print(int)print(3.14); // 调用print(double)
}
示例2:参数数量不同
int sum(int a, int b) {return a + b;
}int sum(int a, int b, int c) {return a + b + c;
}int main() {cout << sum(1, 2);    // 输出3cout << sum(1, 2, 3); // 输出6
}
示例3:参数顺序不同
void show(int a, double b) {cout << "int, double" << endl;
}void show(double a, int b) {cout << "double, int" << endl;
}int main() {show(3, 4.5);   // 调用第一个函数show(4.5, 3);   // 调用第二个函数
}

4、函数重载的解析机制

编译器根据调用时的实参类型和数量选择最匹配的函数,优先级如下:

  1. 精确匹配​(类型完全一致)。
  2. 隐式转换匹配​(如intdouble)。
  3. 用户定义的转换​(如类类型转换运算符)。
示例:匹配优先级
void func(int x) { cout << "int" << endl; }
void func(double x) { cout << "double" << endl; }int main() {func(5);    // 精确匹配func(int)func(5.0);  // 精确匹配func(double)func('a');  // 隐式转换char→int,调用func(int)
}

5、函数重载与const修饰符
1. 顶层const不构成重载
void func(int x) { /* ... */ }
void func(const int x) { /* ... */ } // 错误:重复定义
2. 底层const指针或引用构成重载
void func(int *ptr) { /* ... */ }
void func(const int *ptr) { /* ... */ } // 合法重载void func(int &x) { /* ... */ }
void func(const int &x) { /* ... */ } // 合法重载
示例:const引用重载
void process(string &str) {str += " (modified)";
}void process(const string &str) {cout << str << " (read-only)" << endl;
}int main() {string s = "Hello";const string cs = "Hi";process(s);  // 调用非常量版本process(cs); // 调用常量版本
}

6、函数重载与默认参数

默认参数可能导致二义性调用,需谨慎使用。

void draw(int x, int y = 0) { /* ... */ }
void draw(int x) { /* ... */ }int main() {draw(5); // 错误:两个函数都匹配
}

7、函数重载与模板
1. 函数模板支持隐式重载
template<typename T>
void print(T value) {cout << "Template: " << value << endl;
}void print(int x) {cout << "Non-template: " << x << endl;
}int main() {print(5);      // 调用非模板函数(更匹配)print(3.14);   // 调用模板函数(T=double)
}
2. 特化模板时需注意重载规则
template<>
void print(int x) { // 显式特化cout << "Specialized: " << x << endl;
}

8、类成员函数的重载

类成员函数可以重载,包括构造函数。

class Rectangle {
public:// 构造函数重载Rectangle() { width = height = 0; }Rectangle(int w, int h) : width(w), height(h) {}// 成员函数重载void scale(double factor) { width *= factor; height *= factor; }void scale(int factor) { width *= factor; height *= factor; }
private:int width, height;
};

9、常见错误与解决方案
1. 二义性调用
void func(float x) {}
void func(double x) {}int main() {func(3.14); // 匹配func(double)func(3.14f); // 匹配func(float)func(5);    // 错误:5可转float或double,编译器无法决定
}

解决:显式指定类型,如func(static_cast<float>(5))

2. 隐藏基类重载函数
class Base {
public:void func(int x) {}
};class Derived : public Base {
public:void func(double x) {} // 隐藏Base::func(int)
};int main() {Derived d;d.func(5); // 调用Derived::func(double),Base::func(int)被隐藏
}

解决:使用using Base::func;引入基类重载。


10、小结
  1. 函数重载的核心是参数列表不同,返回类型不影响重载。
  2. 优先通过参数类型、数量或顺序实现重载,避免二义性。
  3. const修饰符在指针或引用参数中可构成重载。
  4. 注意与模板、默认参数和继承的交互关系。
  5. 合理使用重载提升代码可读性,但避免过度设计。
温馨小贴士:重载和作用域的关系
1、函数重载的基本前提

函数重载要求同名函数必须在同一作用域内,并且参数列表不同。
核心规则

  • 只有在同一作用域内,编译器才会将同名函数视为重载候选。

  • 不同作用域中的同名函数会触发名称隐藏​(Name Hiding),而非重载。


2、局部作用域 vs 全局作用域

 局部作用域中声明同名函数会隐藏外部作用域的同名函数

#include <iostream>
using namespace std;void func(int x) { cout << "Global func(int)" << endl; }int main() {func(5); // 调用全局func(int){ // 进入局部作用域void func(double x); // 声明局部作用域的funcfunc(3.14); // 调用局部func(double)// func(5); 错误!全局func(int)被隐藏}func(5); // 再次调用全局func(int)return 0;
}void func(double x) { cout << "Global func(double)" << endl; }

结果

Global func(int)
Global func(double)
Global func(int)

解释

  • 局部作用域中的func(double)声明隐藏了全局作用域的func(int)

  • 全局func(double)实际在局部作用域外定义,但局部作用域内的声明优先。


3、类作用域中的函数重载

类的成员函数重载

class MyClass {
public:void print(int x) { cout << "int: " << x << endl; }void print(double x) { cout << "double: " << x << endl; }
};int main() {MyClass obj;obj.print(5);    // 调用print(int)obj.print(3.14); // 调用print(double)
}

合法重载:两个print函数在同一类作用域内,参数不同。


4、继承中的函数重载与作用域
派生类中同名函数隐藏基类重载
class Base {
public:void func(int x) { cout << "Base::func(int)" << endl; }void func(double x) { cout << "Base::func(double)" << endl; }
};class Derived : public Base {
public:void func(const char* s) { cout << "Derived::func(const char*)" << endl; }
};int main() {Derived d;d.func("Hello"); // 调用Derived::func(const char*)// d.func(5);    错误!Base::func(int)被隐藏d.Base::func(5); // 显式调用基类函数
}

解释

  • 派生类中的func(const char*)隐藏了基类的所有func重载版本。

  • 必须通过Base::作用域运算符显式调用基类函数。

使用using声明恢复基类重载
class Derived : public Base {
public:using Base::func; // 引入基类func的所有重载void func(const char* s) { cout << "Derived::func(const char*)" << endl; }
};int main() {Derived d;d.func(5);       // 调用Base::func(int)d.func(3.14);    // 调用Base::func(double)d.func("Hello"); // 调用Derived::func(const char*)
}

5、命名空间作用域与重载
同一命名空间内的函数重载
namespace NS {void log(int x) { cout << "NS::log(int)" << endl; }void log(double x) { cout << "NS::log(double)" << endl; }
}int main() {NS::log(5);    // 调用log(int)NS::log(3.14); // 调用log(double)
}
不同命名空间的同名函数不构成重载
namespace NS1 { void log(int x) { /* ... */ } }
namespace NS2 { void log(double x) { /* ... */ } }int main() {NS1::log(5);    // 调用NS1::log(int)NS2::log(3.14); // 调用NS2::log(double)// log(5);     错误!未指定命名空间
}

6、函数模板与作用域

模板与非模板函数的重载

void process(int x) { cout << "Non-template" << endl; }template<typename T>
void process(T x) { cout << "Template" << endl; }int main() {process(5);   // 调用非模板函数(更匹配)process(3.14); // 调用模板函数(T=double)
}

规则:非模板函数优先于模板函数匹配。


7、作用域对重载的影响
  1. 同一作用域:函数名相同且参数不同 → 合法重载。

  2. 不同作用域:内层作用域的同名函数会隐藏外层作用域的所有重载版本。

  3. 继承中的重载:派生类函数隐藏基类同名函数,需用using声明恢复。

  4. 解决方案

    • 使用using声明引入外层作用域的重载。

    • 通过作用域运算符(如Base::func)显式调用隐藏函数。

    • 避免在不同作用域中定义同名函数,除非有意隐藏。


关键结论

  • 函数重载仅在相同作用域内有效

  • 作用域隔离会导致名称隐藏,而非重载

  • 合理使用using声明和作用域运算符管理不同作用域中的函数可见性。


五、内联函数(inline)

1、内联函数的作用

目的:通过将函数代码直接插入调用位置,消除函数调用的开销(压栈、跳转、返回),提高程序运行效率。
适用场景:短小且频繁调用的函数(如简单计算、访问类成员)。


2、内联函数的定义
1. 使用inline关键字
inline int add(int a, int b) {return a + b;
}

2. 类内定义的成员函数隐式内联
class Calculator {
public:int multiply(int a, int b) { // 隐式内联return a * b;}
};

小对比:

auto lambda = [](int a, int b) { return a + b; };
cout << lambda(2, 3); // 输出5

3、内联函数的底层逻辑
1. 编译器行为
  • 建议而非强制inline是给编译器的优化建议,最终是否内联由编译器决定。
  • 代码膨胀风险:若函数体较大或频繁调用,内联会导致可执行文件体积增大。
2. 汇编代码对比(示例)
  • 普通函数调用:生成call指令跳转到函数地址。
  • 内联函数:函数体代码直接插入调用位置,无call指令。

4、内联函数的优缺点
优点缺点
减少函数调用开销增加代码体积(多次展开)
避免跳转指令提升执行效率不适合复杂或递归函数
可替代宏(类型安全、易调试)编译器可能忽略inline建议

5、内联函数的限制
  1. 编译器决策权:以下情况编译器通常拒绝内联:
    • 函数体包含循环或递归。
    • 函数体过长(如超过10行)。
    • 虚函数(需动态绑定)。
  2. 头文件要求:内联函数定义必须放在头文件中,确保所有调用位置可见(否则链接错误)。

6、内联函数 vs 宏
特性内联函数宏(#define
处理阶段编译期(语法检查)预处理期(文本替换)
类型安全支持(类型检查)不支持(易引发错误)
调试支持支持(可设置断点)不支持
作用域遵循C++作用域规则全局替换
示例:内联函数替代宏
// 宏的隐患
#define SQUARE(x) ((x) * (x))
int a = 5;
int b = SQUARE(a++); // a被递增两次(未定义行为)// 内联函数(安全)
inline int square(int x) { return x * x; }
int a = 5;
int b = square(a++); // a只递增一次

解释一下:

1. 宏定义 SQUARE(x)

#define SQUARE(x) ((x) * (x))

  • 宏定义是一种简单的文本替换机制。在编译之前,预处理器会将所有出现的 SQUARE(x) 替换为 ((x) * (x))

  • 示例:

    int b = SQUARE(a++); // 被替换为:int b = ((a++) * (a++));

  • 问题:

    • 在表达式 ((a++) * (a++)) 中,a++ 被计算了两次。

    • C++ 标准中明确规定,对于同一个变量,在一个序列点(sequence point)之前对其修改多次是未定义行为。

    • 因此,a 的值可能会被递增两次,导致结果不可预测。


2. 内联函数 square(int x)

inline int square(int x) { return x * x; }

  • 内联函数是一种编译器优化手段。它告诉编译器尽量将函数调用替换为函数体本身,类似于宏展开,但具有类型检查和作用域的优点。

  • 示例:

    int b = square(a++); // 调用时,a++ 只会被计算一次

  • 优点:

    • 参数 a++ 只会被计算一次,并将其结果传递给函数 square

    • 避免了宏定义中可能出现的重复计算问题。

    • 具有类型检查和作用域的安全性。


7、例子
示例1:短小工具函数
// math_utils.h
#ifndef MATH_UTILS_H
#define MATH_UTILS_Hinline int clamp(int value, int min, int max) {return (value < min) ? min : (value > max) ? max : value;
}#endif// main.cpp
#include "math_utils.h"
int main() {int x = clamp(150, 0, 100); // 展开为 (150 < 0) ? 0 : (150 > 100) ? 100 : 150;return 0;
}
示例2:类成员内联函数
class Vector2D {
public:Vector2D(float x, float y) : x(x), y(y) {}inline float magnitude() const {return sqrt(x*x + y*y);}private:float x, y;
};

8、温馨小贴士
  1. 避免滥用内联:优先考虑函数调用开销与代码体积的平衡。
  2. 复杂函数非内联:包含循环、递归或大量代码的函数不宜内联。
  3. 跨模块可见性:内联函数需在头文件中定义,否则导致链接错误。
  4. 现代编译器优化:编译器自动内联简单函数(即使未标记inline)。

9、小结
  1. 内联函数是性能优化工具:通过消除调用开销提升效率。
  2. 短小函数适用:适用于简单、高频调用的场景。
  3. 编译器最终决策inline仅为建议,实际内联由编译器决定。
  4. 替代宏的更安全方案:提供类型安全和调试支持。

六、默认参数

1、默认参数的作用

默认参数允许在函数声明时为参数指定默认值,调用时可省略该参数。
核心优势

  • 简化函数调用,增强代码灵活性。
  • 减少需要重载的函数数量。

2、基本语法与使用
1. 声明默认参数

在函数声明中指定默认值(通常在头文件中):

// 声明函数(默认参数在声明中指定)
void printMessage(const std::string& msg = "Hello, World!");
2. 定义函数

定义时不再重复默认值:

void printMessage(const std::string& msg) {std::cout << msg << std::endl;
}
3. 调用示例
printMessage();       // 输出"Hello, World!"
printMessage("Hi");  // 输出"Hi"

3、默认参数的规则
1. 参数从右向左依次设置默认值
  • 右侧参数必须先设置默认值,左侧参数可选。
// 正确:从右向左设置默认值
void draw(int x, int y = 0, int color = 255);// 错误:左侧参数有默认值,右侧无
void errorFunc(int a = 5, int b); // 编译失败
2. 默认值必须是常量或全局可访问的表达式
  • 允许使用常量、字面量、全局变量或静态变量。
  • 不允许使用局部变量或函数参数
const int DEFAULT_SIZE = 10;
int globalValue = 100;void init(int size = DEFAULT_SIZE, int value = globalValue); // 合法
3. 默认参数只能在函数声明中指定一次
  • 若函数声明和定义分离,​默认参数必须在声明中指定,定义中不可重复。
// 头文件(math_utils.h)
int multiply(int a, int b = 2); // 声明时指定默认值// 源文件(math_utils.cpp)
int multiply(int a, int b) {     // 定义时不写默认值return a * b;
}

4、默认参数与函数重载

默认参数可简化重载,但需避免歧义。

示例:默认参数替代重载
// 用默认参数替代以下两个重载函数
void log(const std::string& msg, bool addTimestamp);
void log(const std::string& msg);// 合并为一个函数
void log(const std::string& msg, bool addTimestamp = false);
错误示例:二义性调用
void connect(int timeout = 10);
void connect();// 调用connect()时,编译器无法确定调用哪个函数

5、默认参数的实际应用
1. 构造函数初始化对象
class Circle {
public:// 构造函数:radius和color都有默认值Circle(double r = 1.0, int c = 0xFF0000) : radius(r), color(c) {}
private:double radius;int color;
};int main() {Circle c1;          // 使用默认半径和颜色Circle c2(5.0);     // 半径5.0,颜色默认Circle c3(3.0, 0x00FF00);
}
2. 灵活配置函数行为
// 绘制矩形:默认颜色为红色,边框可选
void drawRect(int width, int height, const std::string& color = "red", bool hasBorder = true);// 调用
drawRect(100, 200);                   // 使用所有默认值
drawRect(100, 200, "blue");           // 自定义颜色,边框默认
drawRect(100, 200, "green", false);   // 自定义所有参数

6、注意事项
  1. 避免与重载函数冲突:确保默认参数不会导致调用歧义。
  2. 优先在头文件中声明默认值:确保所有调用代码可见。
  3. 慎用复杂默认值:默认值应是简单、明确的默认行为。
  4. 默认参数与C兼容性:C语言不支持默认参数。

7、总结
  1. 默认参数简化调用:减少冗余代码,提升可读性。
  2. 从右向左设置默认值:确保左侧参数在调用时可选。
  3. 声明与定义分离时:默认参数仅在声明中指定。
  4. 合理替代重载:避免过度设计重载函数。

七、函数指针 

1、函数指针的作用

函数指针是指向函数内存地址的变量,允许通过指针动态调用函数。
核心应用

  • 实现回调(Callback)机制。
  • 动态选择函数逻辑(如策略模式)。
  • 与C语言库交互(如qsort中的比较函数)。

2、函数指针的声明与初始化
1. 声明语法
返回类型 (*指针变量名)(参数类型列表);

示例

int (*funcPtr)(int, int); // 指向返回int,参数为两个int的函数
2. 初始化与赋值
// 假设存在函数
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }// 初始化方式1:直接赋值函数名(隐式取地址)
funcPtr = add;// 初始化方式2:显式取地址
funcPtr = &sub;

3、通过函数指针调用函数
1. 直接调用(推荐)
int result = funcPtr(3, 5); // 等价于调用add(3,5)或sub(3,5)
2. 解引用调用
int result = (*funcPtr)(3, 5);
3.用一个完整的例子来看看: 
#include <iostream>
using namespace std;
// 假设存在函数
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }int main(){int add(int a, int b);int sub(int a, int b);int (*ptr1)(int ,int);int (*ptr2)(int ,int);// 初始化方式1:直接赋值函数名(隐式取地址)ptr1 = add;// 初始化方式2:显式取地址ptr2 = &sub;// 使用函数指针调用函数cout << ptr1(5, 3) << endl; // 输出8cout << ptr2(5, 3) << endl; // 输出2return 0;
}

 内存图如下:(有点小问题,但需要表达的意思已经达到了)


4、函数指针的典型应用
1. 回调函数示例
#include <iostream>
using namespace std;// 回调函数类型定义
typedef void (*Callback)(const string&);// 执行任务并回调
void doTask(Callback callback) {cout << "Processing task..." << endl;callback("Task completed!");
}// 具体回调函数
void onComplete(const string& message) {cout << "Callback: " << message << endl;
}int main() {doTask(onComplete);return 0;
}
2. 函数指针数组
int (*operations[])(int, int) = {add, sub};int main() {int a = 10, b = 5;cout << operations[0](a, b) << endl; // 15 (add)cout << operations[1](a, b) << endl; // 5 (sub)
}

5、使用typedefusing简化声明
1. typedef简化
typedef int (*MathFunc)(int, int);
MathFunc funcPtr = add; // 更易读
2. C++11的using别名
using MathFunc = int (*)(int, int);
MathFunc funcPtr = sub;

6、成员函数指针(高级)
1. 声明与调用类成员函数指针
class Calculator {
public:int multiply(int a, int b) { return a * b; }
};int main() {Calculator calc;// 声明成员函数指针int (Calculator::*memFuncPtr)(int, int) = &Calculator::multiply;// 调用int result = (calc.*memFuncPtr)(3, 5); // 输出15return 0;
}
2. 结合对象指针调用
Calculator* pCalc = new Calculator();
int result = (pCalc->*memFuncPtr)(4, 6); // 24
delete pCalc;

7、函数指针与std::function的对比
特性函数指针std::function(C++11+)
类型安全弱(无类型擦除)强(支持类型检查)
携带状态不能能(可绑定lambda、函数对象等)
性能高(直接调用)略低(可能有间接开销)
灵活性仅支持普通函数和静态成员支持函数、lambda、成员函数等

8、注意事项
  1. 类型严格匹配:函数指针类型必须与实际函数签名完全一致。
  2. 空指针检查:调用前需确保指针非空。
  3. C++11+替代方案:优先使用std::function和lambda表达式,除非需要与C兼容。

9、实际应用示例:排序算法回调
#include <algorithm>
#include <vector>
using namespace std;// 比较函数类型
typedef bool (*CompareFunc)(int, int);// 升序比较
bool ascending(int a, int b) { return a < b; }// 降序比较
bool descending(int a, int b) { return a > b; }// 使用函数指针的排序函数
void customSort(vector<int>& vec, CompareFunc comp) {sort(vec.begin(), vec.end(), comp);
}int main() {vector<int> nums = {3, 1, 4, 1, 5};customSort(nums, ascending); // 升序排序customSort(nums, descending); // 降序排序return 0;
}

 解释一下

1. 函数指针类型定义

typedef bool (*CompareFunc)(int, int);

  • 定义了一个名为 CompareFunc 的类型,表示指向返回值为 bool、参数为两个 int 类型的函数的指针。

  • 这种类型定义简化了后续代码中函数指针的声明和使用。


2. 比较函数

bool ascending(int a, int b) { return a < b; }

bool descending(int a, int b) { return a > b; }

  • 定义了两个比较函数:

    • ascending: 判断 a 是否小于 b,用于升序排序。

    • descending: 判断 a 是否大于 b,用于降序排序。

  • 这些函数符合 CompareFunc 的定义,可以作为函数指针传递。


3. 自定义排序函数

void customSort(vector<int>& vec, CompareFunc comp)

{ sort(vec.begin(), vec.end(), comp); }

  • 定义了一个通用的排序函数 customSort,接受以下参数:

    • vec: 需要排序的整数向量。

    • comp: 比较函数的函数指针。

  • 使用 C++ 标准库中的 std::sort 函数进行排序,并将用户提供的比较函数 comp 作为第三个参数传递给 std::sort

  • 通过这种方式,customSort 可以根据不同的比较函数实现升序或降序排序。


4. 主函数调用

int main()

{ vector<int> nums = {3, 1, 4, 1, 5};

customSort(nums, ascending); // 升序排序

customSort(nums, descending); // 降序排序

return 0; }

  • 创建了一个包含整数的向量 nums

  • 调用 customSort 函数两次:

    • 第一次传递 ascending 比较函数,实现升序排序。

    • 第二次传递 descending 比较函数,实现降序排序。


输出结果

假设在每次排序后输出向量内容,程序的输出可能如下:

升序排序后: 1 1 3 4 5

降序排序后: 5 4 3 1 1


10、小结
  1. 函数指针提供动态函数调用能力,是C/C++灵活性的重要体现。
  2. 语法复杂但功能强大,需注意类型匹配和空指针问题。
  3. 现代C++中可结合std::function提升灵活性和安全性
  4. 成员函数指针需结合对象实例使用,适用于面向对象设计模式。

相关文章:

C++:函数(通识版)

一、函数的基础 1.什么是函数&#xff1f;&#xff08;独立的功能单位&#xff09; 函数是C中封装代码逻辑的基本单元&#xff0c;用于执行特定任务。 作用&#xff1a;代码复用、模块化、提高可读性。 2、函数的基本结构 返回类型 函数名(参数列表) {// 函数体return 返回值…...

Spring AI相关的面试题

以下是150道Spring AI相关的面试题目及答案&#xff1a; ### Spring AI基础概念类 **1. 什么是Spring AI&#xff1f;** Spring AI是Spring框架的扩展&#xff0c;旨在简化人工智能模型在Java应用中的集成与使用&#xff0c;提供与Spring生态无缝衔接的工具和抽象&#xff0c…...

无线安灯按钮盒汽车零部件工厂的故障告警与人员调度专家

在汽车零部件制造领域&#xff0c;生产线故障与物料短缺等问题往往引发连锁反应&#xff0c;导致停机损失与成本激增。传统人工巡检与纸质工单模式已难以满足高效生产需求&#xff0c;而无线安灯按钮盒的智能化应用&#xff0c;正成为破解这一难题的关键利器。 一、精准告警&am…...

登录接口带验证码自动化(tesseract-OCR)

登录接口是很多网站和应用程序中必不可少的一部分。为了增加安全性&#xff0c;很多登录接口还会加入验证码的验证步骤&#xff0c;以防止恶意登录行为。 通常&#xff0c;遇到这样情况时有以下解决办法 1、使用万能验证码&#xff1a;如果遇到前台输入的是万能验证码&#xf…...

【Python】pillow库学习笔记2-ImageFilter类和ImageEnhance类

PIL库的ImageFilter类和ImageEnhance类提供了过滤图像和增强图像的方法。 3.ImageFilter类 ImageFilter类共提供10种预定义图像过滤方法&#xff1a; 方法表示描述ImageFilter.BLUR图像的模糊效果ImageFilter.CONTOUR图像的轮廓效果ImageFilter.DETAIL图像的细节效果ImageFi…...

3.Matplotlib:绘图参数文件和绘图的主要函数

一 绘图参数文件 1.绘图参数文件是什么 可以通过在程序中添加代码对参数进行配置&#xff0c;但是如果一个项日对于 Matplotlib 的特性参数总会设置相同的值&#xff0c;就没有必要在每次编写代码的时候都进行相同的配置。在代码之外使用一个永久的文件设定 Matplotlib 参数默认…...

飞书只有阅读权限的文档下载,飞书文档下载没有权限的文件

wx搜索公zhong号&#xff1a;"狮心王"回复"飞书文档保存"下载chrome扩展文件 拿到扩展文件之后给chrome添加扩展...

蓝桥杯C++基础算法-0-1背包(优化为一维)

这段代码实现了0-1 背包问题的动态规划解法&#xff0c;并且使用了滚动数组来优化空间复杂度。以下是代码的详细思路解析&#xff1a; 1. 问题背景 给定 n 个物品&#xff0c;每个物品有其体积 v[i] 和价值 w[i]&#xff0c;以及一个容量为 m 的背包。目标是选择物品使得总价值…...

【开题报告+论文+源码】基于SpringBoot的智能安全与急救知识科普系统设计与实现

项目背景与意义 在全球范围内&#xff0c;安全与急救知识的普及已成为提升公众安全素养、减少意外伤害发生率、提高突发事件应对能力的重要举措。尤其是在当今社会&#xff0c;人们面临的生活、工作环境日益复杂&#xff0c;交通事故、火灾、溺水、突发疾病等各种意外事件的发生…...

Django之旅:第五节--Mysql数据库操作(一)

Django开发操作数据库更简单&#xff0c;内部提供了ORM框架 一、安装第三方模块 pip install mysqlclient注&#xff1a;最新的django框架需要使用mysqlclient模块&#xff0c;之前pymysql模块与django框架有编码兼容问题。 二、ORM 1、ORM可以帮助我们做两件事&#xff1a;…...

蓝桥杯 - 简单 - 布局切换

介绍 为了提高用户体验&#xff0c;网站有时需要多种浏览模式。现在特邀请你为蓝桥官网设计具有经典、浏览和工具三种布局模式。使用户可以根据具体情况选择合适的模式&#xff0c;以便更好地浏览网页内容。 本题需要在已提供的基础项目中使用 JS 完善代码实现布局的切换。 …...

测试用例生成平台通过大模型升级查询功能,生成智能测试用例

在测试工作中&#xff0c;查询功能是各类系统的核心模块&#xff0c;传统的测试用例编写往往耗时且重复。如何让老旧平台焕发新活力&#xff1f;本文将结合大模型技术&#xff0c;通过用户输入的字段信息&#xff0c;自动化生成高效、精准的测试用例。同时&#xff0c;我们还将…...

python每日十题(9)

外存储器的容量一般都比较大&#xff0c;而且大部分可以移动&#xff0c;便于在不同计算机之间进行信息交流。外存储器中数据被读入内存储器后&#xff0c;才能被CPU读取&#xff0c;CPU不能直接访问外存储器。本题答案为A选项。 进程是指一个具有一定独立功能的程序关于某个数…...

macOS 制作dmg磁盘映像安装包

制作dmg磁盘影像安装包需要准备一下材料&#xff1a; 1. 导出的APP 2. 背景图片 3. 应用程序替身 前两种材料很容易得到。 下面介绍一下 应用程序替身制作过程&#xff1a; Finder —> 选中 应用程序 --> 找到顶部菜单栏中 的 前往 ----> 选择上层文件夹选中应用程…...

LeetCode热题100JS(79/100)第十五天|347|295|121|55|45

347. 前 K 个高频元素 题目链接&#xff1a;347. 前 K 个高频元素 难度&#xff1a;中等 刷题状态&#xff1a;1刷 新知识&#xff1a; 解题过程 思考 示例 1: 输入: nums [1,1,1,2,2,3], k 2 输出: [1,2] 没思路&#xff0c;看答案 题解分析 参考题解链接&#xff1a…...

Rust从入门到精通之精通篇:22.Unsafe Rust 详解

Unsafe Rust 详解 在 Rust 的设计哲学中&#xff0c;安全性是核心原则之一。Rust 的所有权系统、借用检查器和类型系统共同保证了内存安全和线程安全。然而&#xff0c;有些底层操作无法通过 Rust 的安全检查机制进行验证&#xff0c;这就是 unsafe Rust 存在的原因。在本章中…...

Three.js 快速入门教程【十八】射线拾取模型——鼠标点击屏幕选中模型或物体

系列文章目录 Three.js 快速入门教程【一】开启你的 3D Web 开发之旅 Three.js 快速入门教程【二】透视投影相机 Three.js 快速入门教程【三】渲染器 Three.js 快速入门教程【四】三维坐标系 Three.js 快速入门教程【五】动画渲染循环 Three.js 快速入门教程【六】相机控件 Or…...

如何下载 Postman?快速指南!

Postman 是一款非常受欢迎的 API 测试工具。它最初是作为一个 Chrome 插件发布&#xff0c;后来发展成为一款独立的跨平台软件&#xff0c;支持 Windows、Mac、Linux 等操作系统。 Postman 怎么下载教程&#xff08;2025最新版&#xff09;&#xff1f;...

Shiro学习(一):Shiro介绍和基本使用

一、Shiro介绍 1、百科对shiro的定义如下&#xff1a; Apache Shiro 一个强大且易于使用的 Java 安全框架&#xff0c;它提供了身份验证、授权、加密和会话管理等功能。Shiro 的设计目标是简化企业级应用程序的安全性开发过程&#xff0c;同时保持代码的简洁和易于维护。 2、…...

【git】基本操作

添加文件进本地仓库 git add 文件名删除文件 git rm 文件名版本回退 git reset [--sort| -- mixed | -- hard] sort选项: 只回退版本库&#xff0c;不回退暂存区和工作区 mixed&#xff08;reset的默认选项&#xff09;: 回退版本库和暂存区&#xff0c;不回退工作区 hard :…...

7.1 分治-快排专题:LeetCode 75. 颜色分类

1. 题目链接 LeetCode 75. 颜色分类 2. 题目描述 给定一个包含红色&#xff08;0&#xff09;、白色&#xff08;1&#xff09;和蓝色&#xff08;2&#xff09;的数组 nums&#xff0c;要求原地对数组进行排序&#xff0c;使得相同颜色的元素相邻&#xff0c;且按红、白、蓝…...

深度解析:TOML、XML、YAML及其他配置/数据格式对比

深度解析&#xff1a;TOML、XML、YAML及其他配置/数据格式对比 在软件开发和系统配置中&#xff0c;选择合适的配置或数据格式至关重要。本文将对比 TOML、XML、YAML 等常见格式&#xff0c;梳理它们的核心特性、适用场景及区别&#xff0c;并扩展介绍其他类似格式&#xff0c…...

开源软件许可证冲突的原因和解决方法

1、什么是开源许可证以及许可证冲突产生的问题 开源软件许可证是一种法律文件&#xff0c;它规定了软件用户、分发者和修改者使用、复制、修改和分发开源软件的权利和义务。开源许可证是由软件的版权所有者&#xff08;通常是开发者或开发团队&#xff09;发布的&#xff0c;它…...

详解java体系实用知识总结

0.java技术能力框架 基础模块应用模块综合模块技术岗位与面试流程常用工具集系统架构设计计算机基础常用框架微服务架构jvm原理缓存容器化多线程队列云计算&#xff08;阿里云/aws&#xff09;设计模式数据库数据结构与算法 1.常用设计模式与应用场景 工厂模式&#xff1a;s…...

node-ddk,electron,主进程通讯,窗口间通讯

node-ddk,electron,主进程通讯,窗口间通讯 https://blog.csdn.net/eli960/article/details/146207062 也可以下载demo直接演示 http://linuxmail.cn/go#node-ddk import 在主进程 import main, { NODEDDK } from "node-ddk/main"在渲染进程 import renderer, …...

kubectl 命令参数详解与示例

kubectl 命令参数详解与示例 kubectl 是 Kubernetes 的命令行工具&#xff0c;用于与 Kubernetes 集群交互。下面我将详细介绍 kubectl 的主要命令参数&#xff0c;并提供相应的使用示例。 一、基础命令 1. kubectl get - 获取资源信息 常用参数&#xff1a; -n, --namesp…...

在 Ubuntu 20.04 上重新启动网络

参考链接&#xff1a; 如何在 Ubuntu 22.04 上重新启动网络 执行以下两条命令&#xff0c;ok sudo nmcli networking off sudo nmcli networking on...

STM32 - 在机器人、自动化领域,LL库相比HAL优势明显

在机器人控制器、电机控制器等领域的开发&#xff0c;需要高实时性、精细化控制或者对代码执行效率、占用空间有较高要求。所以&#xff0c;大家常用的HAL库明显不符合要求。再加上&#xff0c;我们学习一门技术&#xff0c;一定要学会掌握底层的原理。MCU开发的底层就是寄存器…...

【区块链安全 | 第二篇】区块链概念详解

文章目录 概述1. 区块链类型2 区块链五层架构3 账本模型4. 节点&#xff08;Node&#xff09;5. 区块&#xff08;Block&#xff09;6. 区块链&#xff08;Blockchain&#xff09;7. 区块链工作流程 核心技术1. 共识机制2. 智能合约 主要组件1. 交易&#xff08;Transaction&am…...

【开源宝藏】30天学会CSS - DAY6 第六课 流光文字动画

第 0 步&#xff1a;项目结构 lighting-text/├─ index.html└─ style.cssindex.html&#xff1a;包含列表 <ul>&#xff0c;其中每个 <li> 放一个字母或符号。style.css&#xff1a;设置背景、文字样式&#xff0c;以及关键帧动画&#xff08;lighting&#xf…...