Đối tượng hàm (Functors) trong C ++

Apr 09 2022
Trong bài đăng trên blog này, tôi muốn giải thích các đối tượng hàm là gì và cách chúng có thể được sử dụng để giải quyết các vấn đề khác nhau bằng cách đưa ra các ví dụ. Ghi đè toán tử cuộc gọi trong một lớp, operator (), làm cho nó có thể gọi một đối tượng giống như một hàm thông thường.
Ảnh của Luca Bravo trên Unsplash

Trong bài đăng trên blog này, tôi muốn giải thích các đối tượng hàm là gì và cách chúng có thể được sử dụng để giải quyết các vấn đề khác nhau bằng cách đưa ra các ví dụ.

Ghi đè toán tử cuộc gọi trong một lớp, operator ()làm cho nó có thể gọi một đối tượng giống như một hàm thông thường. Các đối tượng hàm (còn được gọi là bộ chức năng) là các đối tượng có thể được gọi giống như các hàm bằng cách ghi đè toán tử cuộc gọi. Chúng thường được định nghĩa là các lớp trong C ++, vì vậy chúng cũng được hưởng các tính năng của các lớp. Chỉ sử dụng các lớp và hàm sẽ giải quyết được nhiều vấn đề và nó ổn. Nhưng đôi khi các đối tượng chức năng dễ sử dụng hơn và giúp chúng ta thoát khỏi gánh nặng của các giải pháp phức tạp.

STL (Thư viện mẫu chuẩn) trong C ++ cung cấp các đối tượng chức năng tích hợp hữu ích và làm cho mọi thứ dễ dàng hơn. Việc hiểu cách chúng được triển khai bên dưới mang lại cái nhìn sâu sắc và khuyến khích chúng ta viết các đối tượng hàm tùy chỉnh nếu các đối tượng hàm tích hợp là không đủ.

Câu hỏi đặt ra là chúng ta thích các đối tượng hàm trong những tình huống nào hơn?

Được rồi, hãy xem qua các ví dụ và xem cách chúng ta có thể sử dụng các đối tượng hàm cho các vấn đề.

Hãy xem xét một vấn đề trong đó chúng ta phải tính tổng các phần tử trong các vectơ riêng biệt trong khi vẫn giữ lại tổng tích lũy cho các phép toán tiếp theo. Giải pháp có thể được viết là:

Trong giải pháp này, một accumulatehàm được định nghĩa để thêm các giá trị đã cho của vectơ vào tích lũy sum. Ngoài ra, một biến tĩnh được đặt tên sumđể giữ lại giá trị tổng cho các lần gọi hàm khác.

Nhưng vấn đề là nếu chúng ta phải tiến hành một phép toán tích lũy khác bắt đầu từ số 0 hoặc đặt lại sumbiến trong accumulatehàm thì sẽ rất khó.

Để giải quyết vấn đề này, sumbiến có thể được định nghĩa là một biến toàn cục, nhưng nó không phải là một thực tiễn tốt và điều này gây ra các vấn đề đồng bộ hóa vì nó có thể sum mở cho các sửa đổi ở bất kỳ đâu trong tệp. Một giải pháp khác sẽ là định nghĩa sumdưới dạng một mảng để chứa các kết quả của nhiều phép toán tích lũy. Nhưng nó phải chịu thêm một tham số đầu vào để phân biệt các hoạt động với nhau và theo dõi các hoạt động cùng một lúc để tránh xung đột.

Có thể vẫn có những giải pháp khác nhau nhưng tất cả chúng đều mang lại những cơ chế phức tạp và hầu hết chúng đều không phải là cách làm tốt. Các đối tượng chức năng phù hợp tốt cho các vấn đề như vậy. Thay vào đó, bạn có thể đạt được cùng một chức năng bằng cách sử dụng các đối tượng chức năng:

Vì Accumulator là một lớp, nó có thể được khởi tạo nhiều khi cần thiết, theo cách này, vòng đời của các hoạt động tích lũy sử dụng các đối tượng Accumulator có thể được quản lý một cách riêng biệt. Không cần phải tạo các cơ chế dư thừa để theo dõi các hoạt động cùng một lúc.

Với các đối tượng hàm, một số thuật toán STL có thể được tùy chỉnh và tôi nghĩ đây là nơi các đối tượng hàm tỏa sáng.

Hãy xem xét một phép toán sắp xếp cho các phần tử của một vectơ theo thứ tự không tăng dần sẽ được thực hiện bằng cách sử dụng hàm STL sort. Hành vi mặc định của sorthàm là sắp xếp các phần tử theo thứ tự không tăng dần. Vì vậy, nó cần được sử dụng để sắp xếp theo thứ tự không tăng dần. Hãy kiểm tra chữ ký của chức năng sắp xếp :

template< class RandomIt, class Compare > 
void sort( RandomIt first, RandomIt last, Compare comp );

C ++ định nghĩa các đối tượng hàm hữu ích theo mặc định và chúng cũng có thể được sử dụng với các hàm STL khác. Một đối tượng hàm lớn hơn được tích hợp sẵn thực hiện phép so sánh nhận hai tham số đầu vào và trả về truenếu tham số đầu tiên lớn hơn tham số thứ hai. Nó thực hiện operator>và vì vậy nó có thể được sử dụng để so sánh các phần tử sortnhư một hàm so sánh. Lưu ý rằng, vì greaterhàm sử dụng các mẫu nên nó phải được sử dụng như greater<int>cho intcác loại.

Theo những dữ kiện này, vấn đề có thể được giải quyết là:

Nhưng thay vì truyền một greaterđối tượng hàm tích hợp sẵn vào thì sortđiều gì sẽ xảy ra nếu chúng ta truyền đối tượng hàm của mình? Hãy thử nó :)

Đối tượng hàm lớn hơn tùy chỉnh ghi đè operator()giống như tất cả các đối tượng hàm và so sánh các tham số đầu vào được truyền tương tự với hàm lớn hơn được tích hợp sẵn:

class GreaterThan 
{
public:
  bool operator()(int x, int y) { return (x > y); }
};

Giải pháp này cho kết quả chính xác giống như giải pháp trước đó. Các đối tượng chức năng cho chúng ta cơ hội tùy chỉnh các thuật toán STL và sử dụng chúng hiệu quả hơn theo nhu cầu của chúng ta.

Các đối tượng chức năng không chỉ được sử dụng với chức năng sắp xếp mà còn các chức năng tích hợp sẵn trong STL , chẳng hạn như transform, và nhiều chức năng khác. Chúng hoạt động giống như các hàm thông thường nhưng cũng được hưởng lợi từ các lớp bằng cách duy trì các hàm và biến thành viên. Những tính năng độc đáo này giúp giải quyết nhiều vấn đề dễ dàng hơn. Trong C ++ 11 trở lên, có một dạng hàm khác được gọi là “biểu thức lambda” và nó có thể hiệu quả hơn và dễ đọc hơn đối với một số vấn đề. Hãy làm cho nó trở thành chủ đề của một bài đăng trên blog khác :)findfor_each

Cảm ơn bạn đã đọc, chúc bạn viết mã vui vẻ!

© Copyright 2021 - 2023 | vngogo.com | All Rights Reserved