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. 機制
  • 將實參的副本傳遞給函數,函數內修改形參不會影響原始數據。

  • ?適用場景:基本數據類型(int,?double等)或小型結構體。

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. 返回基本數據類型(int,?double等)
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. ?成員函數指針需結合對象實例使用,適用于面向對象設計模式。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/74821.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/74821.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/74821.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

sql注入語句學習

說明 注入漏洞作為登頂過web十大漏洞多次的漏洞&#xff0c;危害性不言而喻&#xff0c;其中sql注入就是注入漏洞常用的手段。其形成的原因是由于web在接收傳參數據時&#xff0c;對數據的過濾不夠嚴格&#xff0c;將其帶入到數據庫查詢中&#xff0c;導致用戶可以通過傳參一些…

云鑰科技多通道工業相機解決方案設計

項目應用場景分析與需求挑戰 1. 應用場景 ?目標領域?&#xff1a;工業自動化檢測&#xff08;如精密零件尺寸測量、表面缺陷檢測&#xff09;、3D立體視覺&#xff08;如物體建模、位姿識別&#xff09;、動態運動追蹤&#xff08;如高速生產線監控&#xff09;等。 ?核心…

離散的數據及參數適合用什么算法做模型

離散數據和參數適用的機器學習算法取決于具體任務(分類、回歸、聚類等)、數據特點(稀疏性、類別數量等)以及業務需求。以下是針對離散數據的常用算法分類和選擇建議: 1. 分類任務(離散目標變量) 經典算法 決策樹(ID3/C4.5/CART) 直接處理離散特征,無需編碼,可解釋性…

VMware 安裝 Ubuntu 實戰分享

VMware 安裝 Ubuntu 實戰分享 VMware 是一款強大的虛擬機軟件&#xff0c;廣泛用于多操作系統環境的搭建。本文將詳細介紹如何在 VMware 中安裝 Ubuntu&#xff0c;并分享安裝過程中的常見問題及解決方法。 1. 安裝前的準備工作 (1) 系統要求 主機操作系統&#xff1a;Windo…

基于Promise鏈式調用的多層級請求性能優化

代碼優化-循環嵌套關聯請求 1. 背景 在實際開發中&#xff0c;我們經常會遇到需要嵌套關聯請求的場景&#xff0c;比如&#xff1a; 獲取項目列表獲取項目詳情獲取項目進度 2. 問題 在這種場景下&#xff0c;我們可能會遇到以下問題&#xff1a; 串行請求瀑布流&#xff…

puppeteer+express服務端導出頁面為pdf

以下是開發步驟&#xff1a; 1、創建目錄 puppeteer_demo 目錄&#xff0c;打開目錄 初始化項目&#xff08;命令為&#xff1a;npm init -y&#xff09; 頁面如&#xff1a; 初始化后&#xff0c;項目目錄會出現 package.json 文件 2、安裝 puppeteer &#xff0c;使用命令&a…

GPT-4o圖像生成功能:技術突破與隱憂并存

2025年3月25日&#xff0c;OpenAI正式推出GPT-4o原生圖像生成功能&#xff0c;宣稱其實現了“文本到圖像的終極跨越”。然而&#xff0c;這一被市場追捧的技術在短短72小時內便因用戶需求過載觸發限流&#xff0c;暴露出算力瓶頸與商業化矛盾的尖銳性。這場技術狂歡的背后&…

西域平臺商品詳情接口設計與實現?

接口描述&#xff1a; 該接口用于獲取西域平臺中指定商品的詳細信息&#xff0c;包括商品名稱、價格、庫存、描述、圖片等。 點擊獲取key和secret 接口地址&#xff1a; GET /api/product/detail 請求參數&#xff1a; 參數名 類型 是否必填 描述 productId st…

項目-蒼穹外賣(十五) Apache ECharts+數據統計

一、介紹 二、營業額統計 需求分析和設計&#xff1a; Controller: Service: /*** 營業額統計* param begindate* param enddate* return* */Overridepublic TurnoverReportVO turnoverStatistics(LocalDate begindate, LocalDate enddate) {//創建時間集合List<LocalDate&…

Postgresql導出及導入符合條件的記錄

Postgresql導出及導入符合條件的記錄 Export specific rows from a PostgreSQL table as INSERT SQL script 首先進入psql。 切換到指定資料庫後將資料表中符合條件的記錄導出成csv檔&#xff1a; \c <dbname>; COPY (SELECT * FROM <tablename> WHERE <cond…

體育比分網站開發避坑指南:如何選擇靠譜的數據服務商?(10年行業經驗總結,避免踩坑!)

作為一家專業的體育比分數據服務商&#xff0c;我們接觸過大量客戶&#xff0c;發現很多人在開發體育比分網站或接入數據API時&#xff0c;由于選擇不靠譜的服務商&#xff0c;導致項目延期、數據延遲、售后無響應、隱性收費等問題&#xff0c;最終影響運營效果&#xff0c;甚至…

離心萃取機在畢赤酵母萃取中的應用

在生物醫藥領域&#xff0c;畢赤酵母因其高效表達重組蛋白的能力&#xff0c;成為基因工程的“明星宿主”。然而&#xff0c;如何從復雜的發酵體系中高效提取目標產物&#xff0c;一直是行業痛點。離心萃取機的出現&#xff0c;憑借其高速分離、精準提純的特性&#xff0c;正在…

CNN和LSTM的計算復雜度分析

前言&#xff1a;今天做邊緣計算的時候&#xff0c;在評估模型性能的時候發現NPU計算的大部分時間都花在了LSTM上&#xff0c;使用的是Bi-LSTM&#xff08;耗時占比98%&#xff09;&#xff0c;CNN耗時很短&#xff0c;不禁會思考為什么LSTM會花費這么久時間。 首先聲明一下實…

StarRocks 中 CURRENT_TIMESTAMP 和 current_time 分區過濾問題

背景 本文基于Starrocks 3.3.5 最近在進行Starrocks 跑數據的時候&#xff0c;發現了一個SQL 掃描了所有分區的數據&#xff0c;簡化后的SQL如下&#xff1a; select date_created from tableA where date_createddate_format(current_time(), %Y-%m-%d %H:%i:%S) limit 20其…

從物理學到機器學習:用技術手段量化分析職場被動攻擊行為

從物理學到機器學習:用技術手段量化分析職場被動攻擊行為 1. 從物理系統視角看團隊協作 1.1 團隊系統的能量模型 在熱力學系統中,系統的總能量由動能和勢能組成。類比到團隊協作中,我們可以建立如下模型: class TeamEnergy:def __init__(self, members):self.kinetic = …

Pytroch搭建全連接神經網絡識別MNIST手寫數字數據集

編寫步驟 之前已經記錄國多次的編寫步驟了&#xff0c;無需多言。 &#xff08;1&#xff09;準備數據集 這里我們使用MNIST數據集&#xff0c;有官方下載渠道。我們直接使用torchvison里面提供的數據讀取功能包就行。如果不使用這個&#xff0c;自己像這樣子構建也一樣。 # …

Java 基本數據類型 vs 包裝類(引用數據類型)

一、核心概念對比&#xff08;以 int vs Integer 為例&#xff09; 特性基本數據類型&#xff08;int&#xff09;包裝類&#xff08;Integer&#xff09;數據類型原始值&#xff08;Primitive Value&#xff09;對象&#xff08;Object&#xff09;默認值0null內存位置棧&…

什么是 強化學習(RL):以DQN、PPO等經典模型

什么是 強化學習(RL):以DQN、PPO等經典模型 DQN(深度 Q 網絡)和 PPO(近端策略優化)共同屬于強化學習(Reinforcement Learning,RL)這一領域。強化學習是機器學習中的一個重要分支,其核心在于智能體(Agent)通過與環境進行交互,根據環境反饋的獎勵信號來學習最優的…

【Sql Server】在SQL Server中生成雪花ID(Snowflake ID)

大家好&#xff0c;我是全棧小5&#xff0c;歡迎來到《小5講堂》。 這是《Sql Server》系列文章&#xff0c;每篇文章將以博主理解的角度展開講解。 溫馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不對之處望指正&#xff01; 目錄 前言認識雪花ID…

HTML 表單處理進階:驗證與提交機制的學習心得與進度(一)

引言 在前端開發的廣袤領域中&#xff0c;HTML 表單處理堪稱基石般的存在&#xff0c;是構建交互性 Web 應用不可或缺的關鍵環節。從日常頻繁使用的登錄注冊表單&#xff0c;到功能多樣的搜索欄、反饋表單&#xff0c;HTML 表單如同橋梁&#xff0c;緊密連接著用戶與 Web 應用…