Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
B.Eckel - Thinking in C++, Vol.2, 2nd edition.pdf
Скачиваний:
50
Добавлен:
08.05.2013
Размер:
2.09 Mб
Скачать

#include "PrintSequence.h" #include "../require.h" #include <iostream> #include <algorithm> #include <functional> #include <cstdlib>

using namespace std;

int boundedRand() { return rand() % 100; }

int main(int argc, char* argv[]) { requireArgs(argc, 1, "usage: Binder4 int"); const int sz = 20;

int a[20], b[20] = {0}; generate(a, a + sz, boundedRand); int* end = copy_if(a, a + sz, b,

bind2nd(greater<int>(), atoi(argv[1]))); // Sort for easier viewing:

sort(a, a + sz); sort(b, end);

print(a, a + sz, "array a", " ");

print(b, end, "values greater than yours"," "); } ///:~

Here, an array is filled with random numbers between 0 and 100, and the user provides a value on the command line. In the copy_if( ) call, you can see that the bound argument to bind2nd( ) is the result of the function call atoi( ) (from <cstdlib>).

Function pointer adapters

Any place in an STL algorithm where a function object is required, it’s very conceivable that you’d like to use a function pointer instead. Actually, you can use an ordinary function pointer – that’s how the STL was designed, so that a “function object” can actually be anything that can be dereferenced using an argument list. For example, the rand( ) random number generator can be passed to generate( ) or generate_n( ) as a function pointer, like this:

//: C05:RandGenTest.cpp

// A little test of the random number generator #include <algorithm>

#include <vector> #include <iostream> #include <functional> #include <cstdlib> #include <ctime>

Chapter 15: Multiple Inheritance

273

using namespace std;

int main() {

const int sz = 10000; int v[sz];

srand(time(0)); // Seed the random generator for(int i = 0; i < 300; i++) {

// Using a naked pointer to function: generate(v, v + sz, std::rand);

int count = count_if(v, v + sz, bind2nd(greater<int>(), RAND_MAX/2));

cout << (((double)count)/((double)sz)) * 100 << ' ';

}

} ///:~

The “iterators” in this case are just the starting and past-the-end pointers for the array v, and the generator is just a pointer to the standard library rand( ) function. The program repeatedly generates a group of random numbers, then it uses the STL algorithm count_if( ) and a predicate that tells whether a particular element is greater than RAND_MAX/2. The result is the number of elements that match this criterion; this is divided by the total number of elements and multiplied by 100 to produce the percentage of elements greater than the midpoint. If the random number generator is reasonable, this value should hover at around 50% (of course, there are many other tests to determine if the random number generator is reasonable).

The ptr_fun( ) adapters take a pointer to a function and turn it into a function object. They are not designed for a function that takes no arguments, like the one above (that is, a generator). Instead, they are for unary functions and binary functions. However, these could also be simply passed as if they were function objects, so the ptr_fun( ) adapters might at first appear to be redundant. Here’s an example where using ptr_fun( ) and simply passing the address of the function both produce the same results:

//: C05:PtrFun1.cpp

// Using ptr_fun() for single-argument functions #include <algorithm>

#include <vector> #include <iostream> #include <functional> using namespace std;

char* n[] = { "01.23", "91.370", "56.661", "023.230", "19.959", "1.0", "3.14159" };

const int nsz = sizeof n / sizeof *n;

Chapter 15: Multiple Inheritance

274

template<typename InputIter>

void print(InputIter first, InputIter last) { while(first != last)

cout << *first++ << "\t"; cout << endl;

}

int main() { print(n, n + nsz); vector<double> vd;

transform(n, n + nsz, back_inserter(vd), atof); print(vd.begin(), vd.end());

transform(n,n + nsz,vd.begin(), ptr_fun(atof)); print(vd.begin(), vd.end());

} ///:~

The goal of this program is to convert an array of char* which are ASCII representations of floating-point numbers into a vector<double>. After defining this array and the print( ) template (which encapsulates the act of printing a range of elements), you can see transform( ) used with atof( ) as a “naked” pointer to a function, and then a second time with atof passed to ptr_fun( ). The results are the same. So why bother with ptr_fun( )? Well, the actual effect of ptr_fun( ) is to create a function object with an operator( ). This function object can then be passed to other template adapters, such as binders, to create new function objects. As you’ll see a bit later, the SGI extensions to the STL contain a number of other function templates to enable this, but in the Standard C++ STL there are only the bind1st( ) and bind2nd( ) function templates, and these expect binary function objects as their first arguments. In the above example, only the ptr_fun( ) for a unary function is used, and that doesn’t work with the binders. So ptr_fun( ) used with a unary function in Standard C++ really is redundant (note that Gnu g++ uses the SGI STL).

With a binary function and a binder, things can be a little more interesting. This program produces the squares of the input vector d:

//: C05:PtrFun2.cpp

// Using ptr_fun() for two-argument functions #include <algorithm>

#include <vector> #include <iostream> #include <functional> #include <cmath> using namespace std;

double d[] = { 01.23, 91.370, 56.661, 023.230, 19.959, 1.0, 3.14159 }; const int dsz = sizeof d / sizeof *d;

Chapter 15: Multiple Inheritance

275

int main() { vector<double> vd;

transform(d, d + dsz, back_inserter(vd), bind2nd(ptr_fun(pow), 2.0));

copy(vd.begin(), vd.end(), ostream_iterator<double>(cout, " "));

cout << endl; } ///:~

Here, ptr_fun( ) is indispensable; bind2nd( ) must have a function object as its first argument and a pointer to function won’t cut it.

A trickier problem is that of converting a member function into a function object suitable for using in the STL algorithms. As a simple example, suppose we have the “shape” problem and would like to apply the draw( ) member function to each pointer in a container of Shape:

//: C05:MemFun1.cpp

// Applying pointers to member functions #include "../purge.h"

#include <algorithm> #include <vector> #include <iostream> #include <functional> using namespace std;

class Shape { public:

virtual void draw() = 0; virtual ~Shape() {}

};

class Circle : public Shape { public:

virtual void draw() {

cout << "Circle::Draw()" << endl;

}

~Circle() {

cout << "Circle::~Circle()" << endl;

}

};

class Square : public Shape { public:

virtual void draw() {

Chapter 15: Multiple Inheritance

276

cout << "Square::Draw()" << endl;

}

~Square() {

cout << "Square::~Square()" << endl;

}

};

int main() { vector<Shape*> vs;

vs.push_back(new Circle); vs.push_back(new Square); for_each(vs.begin(), vs.end(),

mem_fun(&Shape::draw)); purge(vs);

} ///:~

The for_each( ) function does just what it sounds like it does: passes each element in the range determined by the first two (iterator) arguments to the function object which is its third argument. In this case we want the function object to be created from one of the member functions of the class itself, and so the function object’s “argument” becomes the pointer to the object that the member function is called for. To produce such a function object, the mem_fun( ) template takes a pointer to member as its argument.

The mem_fun( ) functions are for producing function objects that are called using a pointer to the object that the member function is called for, while mem_fun_ref( ) is used for calling the member function directly for an object. One set of overloads of both mem_fun( ) and mem_fun_ref( ) are for member functions that take zero arguments and one argument, and this is multiplied by two to handle const vs. non-const member functions. However, templates and overloading takes care of sorting all of that out; all you need to remember is when to use mem_fun( ) vs. mem_fun_ref( ).

Suppose you have a container of objects (not pointers) and you want to call a member function that takes an argument. The argument you pass should come from a second container of objects. To accomplish this, the second overloaded form of the transform( ) algorithm is used:

//: C05:MemFun2.cpp

// Applying pointers to member functions #include <algorithm>

#include <vector> #include <iostream> #include <functional> using namespace std;

class Angle { int degrees;

Chapter 15: Multiple Inheritance

277

public:

Angle(int deg) : degrees(deg) {} int mul(int times) {

return degrees *= times;

}

};

int main() { vector<Angle> va;

for(int i = 0; i < 50; i += 10) va.push_back(Angle(i));

int x[] = { 1, 2, 3, 4, 5 }; transform(va.begin(), va.end(), x,

ostream_iterator<int>(cout, " "), mem_fun_ref(&Angle::mul));

cout << endl; } ///:~

Because the container is holding objects, mem_fun_ref( ) must be used with the pointer-to- member function. This version of transform( ) takes the start and end point of the first range (where the objects live), the starting point of second range which holds the arguments to the member function, the destination iterator which in this case is standard output, and the function object to call for each object; this function object is created with mem_fun_ref( ) and the desired pointer to member. Notice the transform( ) and for_each( ) template functions are incomplete; transform( ) requires that the function it calls return a value and there is no for_each( ) that passes two arguments to the function it calls. Thus, you cannot call a member function that returns void and takes an argument using transform( ) or for_each( ).

Any member function works, including those in the Standard libraries. For example, suppose you’d like to read a file and search for blank lines; you can use the string::empty( ) member function like this:

//: C05:FindBlanks.cpp

// Demonstrate mem_fun_ref() with string::empty() #include "../require.h"

#include <algorithm> #include <list> #include <string> #include <fstream> #include <functional> using namespace std;

typedef list<string>::iterator LSI;

Chapter 15: Multiple Inheritance

278

Соседние файлы в предмете Численные методы
  • #
    08.05.20133.99 Mб22A.Menezes, P.van Oorschot,S.Vanstone - HANDBOOK OF APPLIED CRYPTOGRAPHY.djvu
  • #
  • #
    08.05.20135.91 Mб24B.Eckel - Thinking in Java, 3rd edition (beta).pdf
  • #
  • #
    08.05.20136.09 Mб17D.MacKay - Information Theory, Inference, and Learning Algorithms.djvu
  • #
    08.05.20133.85 Mб15DIGITAL Visual Fortran ver.5.0 - Programmers Guide to Fortran.djvu
  • #
    08.05.20131.84 Mб12E.A.Lee, P.Varaiya - Structure and Interpretation of Signals and Systems.djvu