看有的代码可以用函数实现,却用类来封装.
具体例子是这样:
比如STL的list容器,sort的函数可以自定义
一般这样处理:
// comparison, not case sensitive.
bool compare_nocase (string first, string second)
{
unsigned int i=0;
while ( (i<first.length()) && (i<second.length()) )
{
if (tolower(first[i])<tolower(second[i])) return true;
++i;
}
if (first.length()<second.length()) return true;
else return false;
}
mylist.sort(compare_nocase);
这个是c++参考手册的例子,项目中我看到好多地方这么用了
struct mylistSort {
bool operator() (string first, string second) const {
//todo
}
};
mylist.sort(mylistSort());
这样有很明显的好处 还是单纯的风格问题,完全等价?
The ones above are functions, and the ones below are called functors. (I’ll translate it as functors for now)
The most essential difference between the two is that the above is just a process; while the below can contain states. In the latter, closures can be easily implemented.
In C++11, the latter has directly evolved into lambda.
I will use the
sort
you mentioned to give a small example:Simplifying your example, let’s focus on the essential differences. It seems to be equivalent, right?
Then the demand has changed now. When sorting, I only want to sort the elements whose value is greater than
40
. What should I do? You said, I have to write this40
into the function. So what if I say this40
comes from user input? It may also be50
or60
. What should I do?At this point, function seems to be a bit useless. But our functor can still be useful.
The example may be a little weird. . But do you understand what this means?
This struct is actually a functor, which is translated into functor in China. Its advantage is that it can save state.
Let me give you an example. You are using the function pointer of compare_nocase as a parameter. If suddenly there is another place that requires you to compare strings, but this time you are required to ignore the first letter and start the comparison from the second string, then you What should be done?
1. Either you rewrite a compare_nocase2 function, but it will cause a lot of duplicate code.
2. Either you get an int start variable and put it outside compare_nocase. When executing the requirement I just mentioned, first change start=2, and then change the global variable back after execution.
As you can see, it’s not elegant.
Maybe you thought of writing compare_nocase into a class, but it must be a static method.
The solution to functor is very simple.
In this way, you can start the comparison from the first character with mylist.sort(mylistSort(1)); and when you need to ignore the first character and start the comparison with the second letter, you can use mylist.sort(mylistSort(2)) ;
This easily avoids the state management of global variables.
In fact, functor has many other benefits, especially when written with a template, it will play a great role!
By the way, in C++11, you can write it like this
If you are using C++14, you can also change it to auto~~
This usage of myListSort is called a "function object" or "functor". As can be seen from the name, myListSort is a class (or structure), not a function, but its usage is quite similar to a function, that is, it can be "called" by calling a function. The reason is that it overloads the calling operation. symbol"()".
What are the benefits? Let’s take a classic example (given on C++ Primer): If you want to count how many words in an article are longer than 6, then you definitely need to define a function to determine whether the length of a word is more than 6. This function is as follows:
Then pass the function to count_if to count the number of words with a length of more than 6 in the vector words:
This requirement is easy to implement, but the problem is that if 6 is written in the code like above, then there is a new requirement, which is to count the number of words with a length of more than 5, or a word with a length of more than 10 The number of words means that we must reimplement similar functions GT5 and GT10. What if there are more similar needs? Do we need to implement each function one by one?
Using function objects can save us these troubles. Because function objects are class types, they can have their own data members. Define a data member bound, and assign a value to bound when initializing the function object. If you want to count the number of words with a length of more than 5, then bound=5; if it is more than 6, then bound=6, and so on. The specific code is as follows:
Now, if you want to count the number of words with a length of more than 5 in words, the code is as follows:
Similarly, if you want to count the number of words with a length of more than 6 in words, the code is as follows:
Obviously, the advantage of using a function object is that it can have data members, making this "functor" more flexible and easier to use.
For another example, let’s say you want to define a nearest neighbor function. What is "recently"? This means we have to define a distance metric. Suppose we are using weighted Euclidean distance as the metric. In C language, the distance metric can be passed into the nearest neighbor function as a function pointer. In C++, we have a more convenient function object! Therefore, we can put the "weight" in the function object as its data member. According to different requirements, initialize the function object with different weights, so that the nearest neighbor function can produce different results!