C ++函数指针 - 为什么这样做?

时间:2013-11-03 00:10:00

标签: c++ function-pointers

这是一个有点标准的链接列表练习。我有一个函数,它接受另一个函数并循环遍历列表,特别是_node_loop。为什么我使用的语法有效?我还没有看到看起来像它的函数指针语法。

为方便测试而包含完整的代码,但我的问题是关于函数_node_loop_del_node_print_node

#include<stddef.h>
#include <iostream>

template <class T>
class Node {
public:
    T value;
    Node<T>* next;
    Node(T value_, Node<T>* next_) : value(value_), next(next_) {}
    Node(T value_) : value(value_), next(NULL) {}
};



using namespace std;

template <class T>
class List {
public:
    List() : head(NULL) {}

    void prepend(T val) {
        Node<T>* node_ptr = new Node<T>(val, head);
        head = node_ptr;
    }

    void print() {
        cout << "[";
        _node_loop(_print_node);
        cout << "]" << endl;
    }

    ~List() {
        _node_loop(_del_node);
    }

    static List<T> from_array(T tarray[], int N) {
        List<T> ls = List<T>();
        for (int i = 0; i < N; i++) {
            ls.prepend(tarray[i]);
        }
        return ls;
    }

private:
    Node<T>* head;

    void _node_loop(void kernel(Node<T>*)) {
        Node<T>* node_ptr = head;
        Node<T>* tmp;
        while (node_ptr != NULL) {
            tmp = node_ptr;
            node_ptr = node_ptr->next;
            kernel(tmp);
        }
    }

    static void _print_node(Node<T>* node_ptr) {
        if (node_ptr->next == NULL) {
            cout << node_ptr->value;
        }
        else {
            cout << node_ptr->value << ", ";
        }
    }

    static void _del_node(Node<T>* node_ptr) {
        delete node_ptr;
    }
};



int main() {
    int my_array[] = {1, 2, 3, 4, 5};
    List<int> my_list = List<int>::from_array(my_array, 5);
    my_list.print();
    return 0;
}

1 个答案:

答案 0 :(得分:1)

我会对我的评论进行一些扩展。

这里发生了什么?

在解析函数声明后直接调整(或衰减)函数参数类型:

  • some_type 数组已调整为指针some_type
  • 返回some_type 的函数调整为指向返回some_type的函数的指针
  • 顶级 cv-qualifiers 已被删除(例如int const - &gt; int

见[dcl.fct] / 5;保留函数类型的参数,即指向函数的指针包含与原始函数类型相同的参数。

此调整仅在 函数参数类型中进行。它反映了一些可以应用于表达式的隐式转换:

  • some_type 的一个(左值)数组可以转换为指向some_type 指针,指向数组的第一个元素(“array-to-pointer conversion”,[conv.array])
  • a(左值)函数返回some_type 可以转换为指向函数返回some_type (“函数到指针”转换“,[conv.func])
  • 资格转换,例如从intconst int [conv.qual]

关于功能类型

函数类型与函数指针类型不同,但两者都与数组和指针类似。例如,您可以使用函数类型声明(但不定义)函数:

typedef void my_function_type(int, double);

my_function_type f0;  // declare the function `void f0(int, double)`
my_function_type f1;  // declare the function `void f1(int, double)`

int main()
{
    f0(42, 1.2);
    f1(42, 1.2);
}

#include <iostream>
void f0(int, double) { std::cout << "f0\n"; }

// illegal:
//my_function_type f1 { std::cout << "f1\n"; }

void f1(int, double) { std::cout << "f1\n"; }

您可以使用函数类型:

来获取函数的引用和指针
my_function_type& my_ref = f0;
my_function_type* my_ptr = f1;

在最后一行中,使用了函数到指针的转换。它相当于:

my_function_type* my_ptr = &f1;

调用函数指针需要使用一元*。这是语法糖,因为它的含义是明确的,例如

my_ptr(42, 3.14);

因此,以下所有条款均有效:

my_ptr(42, 3.14);
(*my_ptr)(42, 3.14);
my_ref(42, 3.14);
f0(42, 3.14);

N.B。 ()(函数调用)的优先级高于一元*,因此*my_ptr周围的括号是必需的。

由于一元*需要指针类型,您可以根据需要添加任意数量*,请参阅Why do all these crazy function pointer definitions all work? What is really going on?


应用OP的例子

void _node_loop(void kernel(Node<T>*));

此名为_node_loop的成员函数返回void并采用类型为void(Node<T>*)的参数。此参数类型调整为void (*) (Node<T>*),因此_node_loop的声明等同于:

void _node_loop(void (*kernel)(Node<T>*));

在此函数中,使用参数kernel

kernel(tmp);

我们可以通过函数指针调用函数而不使用*。但我们可以更明确(我不推荐):

(*kernel)(tmp);