函数指针在C++遍历函数中的写法和应用(直接在函数中定义函数指针)。
例子,在Qt中,有一个右键菜单,我想把菜单中的节点改为章节。
这里要用到遍历函数:
void traverseMenu(QMenu* menu, bool(*f)(QAction* a))
{
assert(menu && f);
// 遍历当前菜单的所有动作(QAction)
for (QAction* action : menu->actions()) {
if (action->menu()) { // 如果是子菜单
traverseMenu(action->menu(),f); // 递归遍历
}
if (f(action)) return;
}
}
然后traverseMenu用法如下:
qt.traverseMenu(&m, [](QAction* a)->bool {
a->setText(a->text().replace("节点", "章节"));
return false; //遍历全部
});
效果如下:
如果你要把 “彻底删除” 改为“CSDN”,可以这样:
qt.traverseMenu(&m, [](QAction* a)->bool {
if (a->text() == "彻底删除") {
a->setText("csdn");
return true; //结束遍历
}
return false;
});
现在重点来了,现在的每一种数据结构,都支持 for( auto& item: container),你可以把traverseMenu写成模板函数。
template<class T, class fun>
void traverse(const T& container,const fun& f) {
for (auto& item : container) {
if (f(item)) return;
}
}
下面把“csdn” 改为 “彻底删除” 。
qt.traverse(m.actions(), [](QAction* a)->bool {
if (a->text() == "csdn") {
a->setText("彻底删除");
return true;
}
return false;
});
但是如果子菜单中还有子菜单,则要自己检查Action中还有没有子菜单,这是模板函数的及限性。
if (action->menu()) {
}
但是模板函数的通用性很强:
template<class T, class fun>
const T& traverse(T& container, const fun& f) {
for (auto& item : container) {
if (f(item)) return container;
}
return container;
}
int main(int argc, char* argv[])
{
QApplication a(argc, argv); //注意,这里是QApplication
std::vector<int> v = { 1,3,3,5,7 };
std::list<std::string> s = { "abc","345","678","abc"};
traverse(v, [](int& item)->bool {
if (item == 3) { //修改一个
item = 2;
return true;
}
return false;
});
traverse(s, [](std::string& item)->bool {
if (item == "abc") //修改所有
item = "123";
return false;
}
);
for (auto& item : v) {
std::cout << item;
std::cout << "\t";
}
std::cout << "\n";
for (auto& item : s) {
std::cout << item;
std::cout << "\t";
}
return a.exec();
}
上次说过,这只是简单的遍历,还不如直接在代码中写一个循
环,但是在复杂的数据结构中,如树,图,你可能会发觉提早写
一个遍历函数很有用。
另:你可能要准备两个版本的traverse。
/// <summary>
/// 遍历,可修改原值
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="fun"></typeparam>
/// <param name="container"></param>
/// <param name="f"></param>
/// <returns></returns>
/// 创建时间: 2025-03-30 最后一次修改时间:2025-03-30
template<class T, class fun>
T& traverse(T& container,const fun& f) {
for (auto& item : container) {
if (f(item)) return container;
}
return container;
}
/// <summary>
/// 遍历,不可修改源值
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="fun"></typeparam>
/// <param name="container"></param>
/// <param name="f"></param>
/// <returns></returns>
/// 创建时间: 2025-03-30 最后一次修改时间:2025-03-30
template<class T, class fun>
const T& traverse(const T& container, const fun& f) {
for (const auto& item : container) {
if (f(item)) return container;
}
return container;
}
在模板函数中为什么不用函数指针,例如:
template<class T, class value_type>
const T& traverse(T& container, bool (*f)(value_type item)) { ... }
因为使用函数指针有两个缺点:
(1)
-
value_type
需要正确推导为QAction*
,但编译器可能无法准确推断(特别是当m.actions()
返回的类型较复杂时)。 -
即使推导正确,某些编译器可能对 模板 + 函数指针 + lambda 隐式转换 的支持不够完善,导致报错。
(2)
traverse
函数的第二个参数 是一个 函数指针,其类型是:
bool (*f)(value_type item)
这意味着它只能接受 普通函数 或 无捕获的 lambda(即 []
内没有变量的 lambda)。
除非你用 std::function,上次说过原因,下面A,B 两篇文章解释过:
A
Qt控件中函数指针使用的最终版本,使用std::function函数指针使用
B接上一主题,在Qt中,用信号代替函数指针,最终目标都是能直接使用lambda表达式,效果一样。-CSDN博客