讓人壓抑的 C++:記一個函數指針的問題
最近因為項目要求用c++,之前一直很討厭c++,沒辦法只能短時間彌補c++的知識,項目中需要定義一個函數指針類型的vector,本以為很簡單的問題,結果調試了一天,才發現錯在哪里。
多余的std::function
先上代碼吧,這里有一個測試代碼,為什么要有測試代碼?是因為下面的方式我在最開始驗證該種實現時打印的地址是對的,但是之后一段時間就不對了,所以摘出來寫了一個測試代碼。
代碼非常簡單:使用using std::function的方式定義一個函數指針類型func_t,然后實現三個print函數,在main函數中定義一個vector存放三個函數的地址,打印三個函數的實際地址,之后遍歷vector打印存放的元素值。
#include <iostream>
#include <vector>
#include <functional>
// 定義 std::function 類型的函數指針別名
using func_t = std::function<void(int, void*, size_t, size_t, void*)>;
// 示例函數
void print(int x, void* y, size_t a, size_t b, void* c) {
std::cout << "print hello\n";
}
void print1(int x, void* y, size_t a, size_t b, void* c) {
std::cout << "print1 hello\n";
}
void print2(int x, void* y, size_t a, size_t b, void* c) {
std::cout << "print2 hello\n";
}
int main() {
// 創建一個存儲 std::function 類型的函數指針對象的 std::vector
std::vector<func_t> vec;
// 使用 push_back 將函數指針對象添加到 std::vector 中
vec.push_back(print);
vec.push_back(print1);
vec.push_back(print2);
printf("%x, %x, %x\n", print, print1, print2);
// 遍歷 std::vector 并依次調用存儲的函數指針對象
for (const auto& func : vec) {
// 調用函數指針對象
//func(0, nullptr, 0, 0, nullptr);
printf("%x.\n", func);
}
return 0;
}
執行后的結果:
我最開始的理解是vector內部存放的地址就是三個函數的地址。結果打印的結果意料之外啊,居然一樣,我嘗試在for循環遍歷時執行該地址函數,結果還能正常運行。最開始以為是vector遍歷取值的問題,后來經過一番驗證沒問題,最后鎖定要函數指針定義上。
我嘗試切換一種函數指針定義,使用我最原始的方式:
// 定義 std::function 類型的函數指針別名
//using func_t = std::function<void(int, void*, size_t, size_t, void*)>;
using func_t = void (*)(int, void*, size_t, size_t, void*);
運行后發現這次是對的了:
最后經過一番查找,得出結論如下:
實際上,std::function 存儲函數指針時,不直接存儲函數指針本身的地址,而是存儲了函數指針對象的一些信息,因此直接使用 %x 來打印 std::function 存儲的函數指針可能無法獲得正確的地址。
在標準庫 中,std::function 是一個函數包裝器,它可以包含各種可調用對象(函數指針、函數對象、成員函數指針、Lambda 表達式等)。因此,std::function 內部存儲了被包裝對象的地址以及其他信息,而不是直接將被包裝對象的地址暴露給用戶。
由于 std::function 對象的內部結構不同于原始函數指針, std::function 對象存儲了更多的信息,所以直接打印 std::function 對象的地址并不會得到和原始函數指針相同的值,打印它的地址并不等同于打印函數指針的地址。
所以,如果需要存儲函數指針并在之后通過 std::function 來調用它們,可以直接通過 std::function 來調用并且可以得到預期的結果,但是打印地址是不保證能夠得到和原始函數指針相同的地址(這也是我遇到了幾次和原始函數指針一致的時候,這也是造成我更迷茫的原因)。
那為什么打印的值一樣呢?
因為在遍歷 std::vector<std::function> 時,即使它們指向不同的函數,它們的內部指針值可能是相同的,這是因為 std::function 可以包裝不同的可調用對象,但它們內部可能使用相同的機制來存儲函數指針或者函數對象的地址。因此,打印 std::function 內部存儲的函數指針值可能會得到相同的結果。但這不應該影響 std::function 執行其持有的不同函數的能力。
總結
如果你也需要直接獲取存儲的函數指針的地址(C語言的習慣),最好還是直接使用原始的函數指針,而不是通過 std::function 來存儲和獲取函數指針的地址。