您可以如下描述一个简单的列表:
如您所见,这个列表非常简单;例如,它不允许插入或删除。设计一个列表类来表示这个抽象的type.You,应该提供一个list.h头文件和一个带有类方法的list.cpp文件,implementations.You也应该创建一个利用您的设计的简短程序。
保持列表规范简单的主要原因是简化这个编程,exercise.You可以将列表实现为数组,或者,如果您熟悉数据类型,则将其实现为链接列表。但是公共接口不应该依赖于您的choice.That is,公共接口不应该有数组索引、指向节点的指针等等。应该用创建列表、将项添加到列表的一般概念来表示,因此on.The处理访问每个项和执行操作的通常方法是使用函数指针作为参数:
void visit(void (*pf)(Item &));这里pf指向引用Item参数的函数(而不是成员函数),其中Item是list.The访问()函数中项的类型,该函数应用于list.You中的每个项,可以使用Stack类作为一般指南。
我想知道的是,为什么我要使用指针到函数?使用通常的成员函数和使用指针到函数作为参数的函数有什么区别?(在本例中,使用void visit(void (*pf)(Item &)))?
发布于 2015-02-03 12:32:37
函数指针
假设您有一个函数,它接受一个数字并对其进行平方并返回它。你有一个名单,你想要的每个成员的平方。你怎么做到的?
两者执行相同的任务。您可能会认为前一种情况更容易实现。毕竟,您不必处理函数指针。
然而,如果你有20个函数,可以双倍,三重,立方体,正方形,等等,一个参数传递给他们。如果您遵循第一条路线,您必须编写20个不同的函数(可能有不同的名称)。然而,现在,后者是有意义的。您只需声明单个函数。并通过指针传递数组和20个函数中的任何一个来调用转换器函数来完成您的任务。
一个例子是std::transform中的C++ STL。
工作内容:
#include <iostream>
#include <vector>
typedef double (*function)(double);
void transformer(std::vector<double>& to_transform, function f)
{
for(auto it = to_transform.begin(); it != to_transform.end(); ++it)
*it = f(*it);
}
void print(const std::vector<double>& v)
{
std::cout << "[ ";
for(double val : v)
std::cout << val << " ";
std::cout << "]" ;
}
double f1(double a) { return a*2; }
double f2(double a) { return a*3; }
double f3(double a) { return a/2; }
double f4(double a) { return a*a*a; }
int main() {
std::vector<double> array = { 2.3, 5.6, 4.5, 7.8, 2.3 };
std::vector<function> function_ptrs = { &f1, &f2, &f3, &f4 };
std::size_t val ;
std::cout << "The original : " ;
print(array);
std::cout << "\nChoose a function (1-4) : ";
std::cin >> val;
std::cout << "The array after applying function " << val << " is : ";
transformer(array, function_ptrs[(val - 1) % function_ptrs.size()]);
print(array);
return 0;
}我假设您有一个符合C++11的编译器。上面的代码有四个函数,其中包含一个双,并以某种方式对其进行转换。transformer函数将这样的函数应用于双倍向量。函数指针也存储在向量中-是的,一个函数指针数组。这些函数可以通过索引访问普通元素时调用。在选择一个选项时,由transformer元素wise在双重向量上调用和执行适当的函数。
您可以使用模板(而不是固定的双倍)和使用来自STL的std::transform来进一步改进它。
C++11 Lambda表达式
另外,对于C++11,您应该更喜欢lambda而不是函数指针。Lambdas被写成
[ ... capture list ... ] ( params ) -> return_type (optional) { body }使用lambda的解决方案如下所示:
#include <iostream>
#include <algorithm>
#include <vector>
template <typename T>
void print(const std::vector<T>& v)
{
std::cout << "[ ";
for(T val : v)
std::cout << val << " ";
std::cout << "]" ;
}
int main() {
std::vector<double> array = { 2.3, 5.6, 4.5, 7.8, 2.3 };
std::cout << "The original : " ;
print(array);
std::cout << "\nThe array after transforming : " ;
std::transform(array.begin(), array.end(), array.begin(),
[](double x) { return x * x; });
print(array);
return 0;
}函数对象
您可以声明自己的类,它只是重载()操作符(使对象可调用),并作为函数指针执行相同的任务(可以传递给函数并调用),即在这种情况下,类看起来如下:
class double_the_value
{
double operator()(double val) const { return val * 2.0 ; }
};
double_the_value f ;
std::cout << f(3.0) ; // outputs 6.0实际使用的是std::unordered_map容器,如果您使用自己的类类型作为密钥,则需要提供一个键hasher --它可以是一个函数对象。this答案详细说明了这一点。
发布于 2015-02-03 12:47:11
当您创建一个真正的抽象列表时,您不知道需要对对象调用哪些函数,所以您通常的成员函数是不够的。你不能把它们都写下来。
此模式的一个常见且更容易的替代方法是返回完整集合的副本或将迭代器公开到第一个和最后一个元素。但是,这可能会导致性能问题,并且可能会有风险--副本可能很昂贵,而且通常是不必要的,如果使用迭代器,如果集合在您下面发生更改,它们可能会变得无效。访问者模式通过为您提供更好的封装来隐藏所有这些,并将迭代循环保留在类中,它通常属于该类。
这是一个额外的抽象层。如果您有一个项目集合,您通常希望对该集合中的某些(或全部)项进行处理。访问者模式允许您检查每个项目,然后做一些事情。
https://stackoverflow.com/questions/28298913
复制相似问题