Читаем Thinking In C++. Volume 2: Practical Programming полностью

Sometimes you’d like to create your own manipulators, and it turns out to be remarkably simple. A zero-argument manipulator such as endl is simply a function that takes as its argument an ostream reference and returns an ostream reference. The declaration for endl is.

ostream& endl(ostream&);

Now, when you say:.

cout << "howdy" << endl;

the endl produces the address of that function. So the compiler asks, "Is there a function I can call that takes the address of a function as its argument?" Predefined functions in do this; they’re called applicators (because they apply a function to a stream). The applicator calls its function argument, passing it the ostream object as its argument. You don’t need to know how applicators work to create your own manipulator; you only need to know that they exist. Nonetheless, they’re simple. Here’s the (simplified) code for an ostream applicator:

ostream& ostream::operator<<(ostream& (*pf)(ostream&)) {

  return pf(*this);

}

The actual definition is a little more complicated since it involves templates, but this code illustrates the technique. When a function such as *pf (that takes a stream parameter and returns a stream reference) is inserted into a stream, this applicator function is called, which in turn executes the function to which pf points. Applicators for ios_base, basic_ios, basic_ostream, and basic_istream are predefined in the standard C++ library.

To illustrate the process, here’s a trivial example that creates a manipulator called nl that is equivalent to just inserting a newline into a stream (i.e., no flushing of the stream occurs, as with endl):.

//: C04:nl.cpp

// Creating a manipulator

#include

using namespace std;

ostream& nl(ostream& os) {

  return os << '\n';

}

int main() {

  cout << "newlines" << nl << "between" << nl

       << "each" << nl << "word" << nl;

} ///:~

When you insert nl into an output stream, such as cout, the following sequence of calls ensues:

cout.operator<<(nl) è nl(cout)

The expression

os << '\n';

inside nl( ) calls ostream::operator(char), which of course returns the stream, which is what is ultimately returned from nl( ).[45] 

<p>Effectors</p>

As you’ve seen, zero-argument manipulators are easy to create. But what if you want to create a manipulator that takes arguments? If you inspect the header, you’ll see a type called smanip, which is what the manipulators with arguments return. You might be tempted to somehow use that type to define your own manipulators, but don’t give in to the temptation. The smanip type is implementation-dependent, so using it would not be portable. Fortunately, you can define such manipulators in a straightforward way without any special machinery, based on a technique introduced by Jerry Schwarz, called an effector.[46] An effector is a simple class whose constructor formats a string representing the desired operation, along with an overloaded operator<< to insert that string into a stream. Here’s an example with two effectors. The first outputs a truncated character string, and the second prints a number in binary.

//: C04:Effector.cpp

// Jerry Schwarz's "effectors"

#include

#include   // For max()

#include

#include

using namespace std;

// Put out a prefix of a string:

class Fixw {

  string str;

public:

  Fixw(const string& s, int width)

    : str(s, 0, width) {}

  friend ostream&

  operator<<(ostream& os, const Fixw& fw) {

    return os << fw.str;

  }

};

// Print a number in binary:

typedef unsigned long ulong;

class Bin {

  ulong n;

public:

  Bin(ulong nn) { n = nn; }

  friend ostream& operator<<(ostream& os, const Bin& b) {

    const ulong ULMAX = numeric_limits::max();

    ulong bit = ~(ULMAX >> 1); // Top bit set

    while(bit) {

      os << (b.n & bit ? '1' : '0');

      bit >>= 1;

    }

    return os;

  }

};

int main() {

  string words =

    "Things that make us happy, make us wise";

  for(int i = words.size(); --i >= 0;) {

    ostringstream s;

    s << Fixw(words, i);

    assert(s.str() == words.substr(0, i));

  }

  ostringstream xs, ys;

  xs << Bin(0xCAFEBABEUL);

  assert(xs.str() ==

    "1100""1010""1111""1110""1011""1010""1011""1110");

  ys << Bin(0x76543210UL);

  assert(ys.str() ==

    "0111""0110""0101""0100""0011""0010""0001""0000");

} ///:~

The constructor for Fixw creates a shortened copy of its char* argument, and the destructor releases the memory created for this copy. The overloaded operator<< takes the contents of its second argument, the Fixw object, inserts it into the first argument, the ostream, and then returns the ostream so that it can be used in a chained expression. When you use Fixw in an expression like this:.

cout << Fixw(string, i) << endl;

a temporary object is created by the call to the Fixw constructor, and that temporary object is passed to operator<<. The effect is that of a manipulator with arguments. The temporary Fixw object persists until the end of the statement.

Перейти на страницу:

Похожие книги

3ds Max 2008
3ds Max 2008

Одни уверены, что нет лучшего способа обучения 3ds Мах, чем прочитать хорошую книгу. Другие склоняются к тому, что эффективнее учиться у преподавателя, который показывает, что и как нужно делать. Данное издание объединяет оба подхода. Его цель – сделать освоение 3ds Мах 2008 максимально быстрым и результативным. Часто после изучения книги у читателя возникают вопросы, почему не получился тот или иной пример. Видеокурс – это гарантия, что такие вопросы не возникнут: ведь автор не только рассказывает, но и показывает, как нужно работать в 3ds Мах.В отличие от большинства интерактивных курсов, где работа в 3ds Мах иллюстрируется на кубиках-шариках, данный видеокурс полностью практический. Все приемы работы с инструментами 3ds Мах 2008 показаны на конкретных примерах, благодаря чему после просмотра курса читатель сможет самостоятельно выполнять даже сложные проекты.

Владимир Антонович Верстак , Владимир Верстак

Программирование, программы, базы данных / Программное обеспечение / Книги по IT