Выбрать главу

//: C06:Transform.cpp

// Use of STL transform() algorithm

#include "Counted.h"

#include <iostream>

#include <vector>

#include <algorithm>

using namespace std;

template<class T>

T* deleteP(T* x) { delete x; return 0; }

#ifdef _MSC_VER

// Microsoft needs explicit instantiation

template Counted* deleteP(Counted* x);

#endif

template<class T> struct Deleter {

  T* operator()(T* x) { delete x; return 0; }

};

int main() {

  CountedVector cv("one");

  transform(cv.begin(), cv.end(), cv.begin(),

    deleteP<Counted>);

  CountedVector cv2("two");

  transform(cv2.begin(), cv2.end(), cv2.begin(),

    Deleter<Counted>());

} ///:~

This shows both approaches: using a template function or a templatized function object. After the call to transform( ), the vector contains five null pointers, which is safer since any duplicate deletes will have no effect.

One thing you cannot do is delete every pointer in a collection without wrapping the call to delete inside a function or an object. That is, you do the following:.

for_each(a.begin(), a.end(), ptr_fun(operator delete));

This has the same problem as the call to destroy ( ) did earlier: operator delete( ) takes a void*, but iterators aren’t void pointers (or pointers at all). Even if you could make it compile, what you’d get is a sequence of calls to the function that releases the storage. You will not get the effect of calling delete for each pointer in a, however; the destructor will not be called. This is typically not what you want, so you will need wrap your calls to delete.

In the previous example of for_each( ), the return value of the algorithm was ignored. This return value is the function that is passed in to for_each( ). If the function is just a pointer to a function, the return value is not very useful, but if it is a function object, that function object may have internal member data that it uses to accumulate information about all the objects that it sees during for_each( ).

For example, consider a simple model of inventory. Each Inventory object has the type of product it represents (here, single characters will be used for product names), the quantity of that product, and the price of each item:.

//: C06:Inventory.h

#ifndef INVENTORY_H

#define INVENTORY_H

#include <iostream>

#include <cstdlib>

#include <ctime>

#ifndef _MSC_VER

// Microsoft namespace work-around

using std::rand;

using std::srand;

using std::time;

#endif

class Inventory {

  char item;

  int quantity;

  int value;

public:

  Inventory(char it, int quant, int val)

    : item(it), quantity(quant), value(val) {}

  // Synthesized operator= & copy-constructor OK

  char getItem() const { return item; }

  int getQuantity() const { return quantity; }

  void setQuantity(int q) { quantity = q; }

  int getValue() const { return value; }

  void setValue(int val) { value = val; }

  friend std::ostream& operator<<(

    std::ostream& os, const Inventory& inv) {

    return os << inv.item << ": "

      << "quantity " << inv.quantity

      << ", value " << inv.value;

  }

};

// A generator:

struct InvenGen {

  InvenGen() { srand(time(0)); }

  Inventory operator()() {

    static char c = 'a';

    int q = rand() % 100;

    int v = rand() % 500;

    return Inventory(c++, q, v);

  }

};

#endif // INVENTORY_H ///:~

Member functions get the item name and get and set quantity and value. An operator<< prints the Inventory object to an ostream. A generator creates objects that have sequentially labeled items and random quantities and values.

To find out the total number of items and total value, you can create a function object to use with for_each( ) that has data members to hold the totals:.

//: C06:CalcInventory.cpp

// More use of for_each()

#include "Inventory.h"

#include "PrintSequence.h"

#include <vector>

#include <algorithm>

using namespace std;

// To calculate inventory totals:

class InvAccum {

  int quantity;

  int value;

public:

  InvAccum() : quantity(0), value(0) {}

  void operator()(const Inventory& inv) {

    quantity += inv.getQuantity();

    value += inv.getQuantity() * inv.getValue();

  }

  friend ostream&

  operator<<(ostream& os, const InvAccum& ia) {

    return os << "total quantity: "

      << ia.quantity

      << ", total value: " << ia.value;

  }

};

int main() {

  vector<Inventory> vi;

  generate_n(back_inserter(vi), 15, InvenGen());

  print(vi.begin(), vi.end(), "vi");

  InvAccum ia = for_each(vi.begin(),vi.end(),

    InvAccum());

  cout << ia << endl;

} ///:~

InvAccum’s operator( ) takes a single argument, as required by for_each( ). As for_each( ) moves through its range, it takes each object in that range and passes it to InvAccum::operator( ), which performs calculations and saves the result. At the end of this process, for_each( ) returns the InvAccum object that you can then examine; in this case, it is simply printed.

You can do most things to the Inventory objects using for_each( ). For example, for_each( ) can handily increase all the prices by 10%. But you’ll notice that the Inventory objects have no way to change the item value. The programmers who designed Inventory thought this was a good idea. After all, why would you want to change the name of an item? But marketing has decided that they want a "new, improved" look by changing all the item names to uppercase; they’ve done studies and determined that the new names will boost sales (well, marketing has to have something to do …). So for_each( ) will not work here, but transform( ) wilclass="underline"