Không có gì đặc biệt "nhanh" về các con trỏ hàm. Chúng cho phép bạn gọi một hàm được xác định trong thời gian chạy. Nhưng bạn có chính xác cùng một chi phí như bạn nhận được từ bất kỳ cuộc gọi chức năng khác (cộng với con trỏ bổ sung). Hơn nữa, kể từ khi chức năng gọi được xác định trong thời gian chạy, trình biên dịch thường có thể không inline gọi hàm như nó có thể bất cứ nơi nào khác. Như vậy, các con trỏ hàm có thể trong một số trường hợp thêm chậm hơn đáng kể so với cuộc gọi hàm thông thường.
Con trỏ hàm không có gì liên quan đến hiệu suất và không bao giờ được sử dụng để đạt được hiệu suất. Thay vào đó, chúng là một cái gật đầu rất nhỏ với mô hình lập trình chức năng, trong đó chúng cho phép bạn truyền một hàm xung quanh như tham số hoặc giá trị trả về trong một hàm khác. Quay lại đầu trang Cung cấp Phản hồi THÔNG TIN THÊM
Ví dụ đơn giản là hàm phân loại chung. Nó phải có một số cách để so sánh hai phần tử để xác định cách chúng được sắp xếp. Đây có thể là một con trỏ hàm được truyền cho hàm sắp xếp, và trên thực tế C++ std :: sort() có thể được sử dụng chính xác như thế. Nếu bạn yêu cầu nó sắp xếp các chuỗi của một kiểu không xác định toán tử nhỏ hơn, bạn phải truyền vào một con trỏ hàm mà nó có thể gọi để thực hiện so sánh.
Và điều này dẫn chúng ta đến một sự thay thế tuyệt vời. Trong C++, bạn không bị giới hạn đối với các con trỏ hàm. Bạn thường sử dụng functors thay vào đó - đó là, các lớp quá tải toán tử(), để chúng có thể được gọi là "như" chúng là các hàm. Functors có một vài lợi thế lớn so với con trỏ hàm:
- Chúng mang lại sự linh hoạt hơn: chúng là các lớp chính thức, với hàm tạo, hàm hủy và biến thành viên. Chúng có thể duy trì trạng thái và chúng có thể hiển thị các hàm thành viên khác mà mã xung quanh có thể gọi.
- Chúng nhanh hơn: không giống như con trỏ hàm, loại của chúng chỉ mã hóa chữ ký của hàm (một biến kiểu
void (*)(int)
có thể là bất kỳ hàm nào mất khoảng int và trả về. Chúng tôi không thể biết cái nào), Kiểu của functor mã hóa hàm chính xác cần được gọi (Vì một functor là một lớp, gọi nó là C, chúng ta biết rằng hàm cần gọi là, và sẽ luôn là, C :: operator()). Và điều này có nghĩa là trình biên dịch có thể nội tuyến cuộc gọi hàm. Đó là phép thuật làm cho chuẩn std :: sắp xếp nhanh như hàm phân loại được mã hóa bằng tay của bạn được thiết kế đặc biệt cho kiểu dữ liệu của bạn. Trình biên dịch có thể loại bỏ tất cả các chi phí của việc gọi một hàm do người dùng định nghĩa.
- Chúng an toàn hơn: Có rất ít loại an toàn trong con trỏ hàm. Bạn không có đảm bảo rằng nó trỏ đến một chức năng hợp lệ. Nó có thể là NULL. Và hầu hết các vấn đề với con trỏ cũng áp dụng cho các con trỏ hàm. Chúng nguy hiểm và dễ bị lỗi.
Con trỏ hàm (trong C) hoặc functors (trong C++) hoặc đại biểu (trong C#) tất cả giải quyết cùng một vấn đề, với các mức độ sang trọng và linh hoạt khác nhau: Chúng cho phép bạn xử lý các hàm như giá trị hạng nhất, chuyển chúng xung quanh khi bạn thực hiện bất kỳ biến nào khác. Bạn có thể truyền chức năng cho một chức năng khác, và nó sẽ gọi hàm của bạn vào các thời điểm cụ thể (khi hết giờ, khi cửa sổ cần vẽ lại hoặc khi cần so sánh hai phần tử trong mảng của bạn)
Theo tôi biết (và tôi có thể sai, bởi vì tôi đã không làm việc với Java cho các lứa tuổi), Java không có một tương đương trực tiếp. Thay vào đó, bạn phải tạo một lớp, thực hiện một giao diện và định nghĩa một hàm (gọi nó là Execute(), ví dụ). Và sau đó thay vì gọi hàm do người dùng cung cấp (trong hình dạng của một con trỏ hàm, hàm functor hoặc delegate), bạn gọi hàm foo.Execute(). Tương tự như việc thực hiện C++ về nguyên tắc, nhưng không có tính tổng quát của các mẫu C++, và không có cú pháp hàm cho phép bạn xử lý các con trỏ hàm và hàm functors theo cùng một cách.
Vì vậy, đó là nơi bạn sử dụng các con trỏ hàm: Khi không có các lựa chọn thay thế phức tạp hơn (ví dụ: bạn bị mắc kẹt trong C), và bạn cần chuyển một hàm này sang hàm khác. Kịch bản phổ biến nhất là gọi lại. Bạn định nghĩa một hàm F mà bạn muốn hệ thống gọi khi X xảy ra. Vì vậy, bạn tạo một con trỏ hàm trỏ tới F và chuyển nó tới hệ thống được đề cập.
Vì vậy, thực sự, hãy quên John Carmack và đừng cho rằng bất cứ điều gì bạn thấy trong mã của mình sẽ làm cho mã của bạn trở nên tốt hơn nếu bạn sao chép nó. Ông đã sử dụng các con trỏ hàm vì các trò chơi mà bạn đề cập được viết bằng C, trong đó các lựa chọn thay thế cao không có sẵn, và không phải vì chúng là một thành phần ma thuật mà sự tồn tại duy nhất làm cho mã chạy nhanh hơn.
Viết một số mã bằng JavaScript trong đó các hàm là hạng nhất và bạn sẽ yêu. Chỉ là một ví dụ nhỏ: Tôi có một lớp học theo dõi một tập tin với các thiết lập cho những thay đổi. Khi bạn tạo lớp, bạn chuyển cho nó một ủy nhiệm để gọi mỗi khi tệp thay đổi. – core