Слайд 2Типы адаптеров итераторов
Адаптеры - шаблонные классы, которые обеспечивают отображения интерфейса. Например,
insert_iterator обеспечивает контейнер интерфейсом итератора вывода.
Иногда адаптеры итераторов называют адаптерами экземпляра для итераторов.
Для итераторов существуют два основных типа адаптеров:
- итераторы вставки (insert)
- обратные (reverse) итераторы.
Кроме того, имеются адаптеры потоковых итераторов. Со стандарта С++11 появился адаптер итератора перемещения.
Слайд 3Вставка
Итератор вставки inserter является общим в том смысле, что мы указываем
позицию, в которой будет происходить вставка элементов. Если вставка в контейнер L должна происходить в конце, мы можем написать L.end(), как в следующей программе:
Слайд 4Пример
// Копирование вектора с помощью итератора вставки.
#include
#include
#include
using namespace
std;
int main() {
int a [4] = {10, 20, 30, 40};
vector v(a, a+4);
list L(2, 123);
copy( v.begin(), v.end(), inserter (L, L.end()));
list::iterator i;
for (i=L.begin(); i != L.end(); ++i)
cout *i << " "; // Вывод: 123 123 10 20 30 40
cout << endl;
return 0;
}
Слайд 5Еще пример
#include
#include
#include
#include
int main ()
{
std::list foo, bar;
for (int i=1; i<=5; i++)
{ foo.push_back(i); bar.push_back(i*10); }
std::list::iterator it = foo.begin();
advance (it,3);
std::copy ( bar.begin(), bar.end(), std::inserter (foo, it) );
std::cout << "foo contains:";
for ( std::list::iterator it = foo.begin(); it!= foo.end(); ++it )
std::cout << ' ' << *it;
std::cout << '\n'; return 0; }
Output:
1 2 3 10 20 30 40 50 4 5
Слайд 6Поскольку вставка в конце контейнера является часто встречающейся операцией, для нее существует
специальный итератор вставки, называемый back_inserter.
Приведенная выше программа работает точно так же, если мы заменим вызов алгоритма сору на вызов:
copy ( v. begin () , v.end() , back_inserter (L) ) ;
Так как back_inserter всегда добавляет элементы в конец, он требует в качестве единственного аргумента контейнер.
Слайд 7Пример back_inserter
#include
#include
#include
#include
int main ()
{
std::vector foo, bar;
for (int i=1; i<=5; i++)
{ foo.push_back(i); bar.push_back(i*10); }
std::copy (bar.begin(), bar.end(), back_inserter(foo));
std::cout << "foo contains:";
for ( std::vector::iterator it = foo.begin(); it!= foo.end(); ++it )
std::cout << ‘ ' << *it;
std::cout << '\n'; return 0;
}
Output:
foo contains: 1 2 3 4 5 10 20 30 40 50
Слайд 8Еще один итератор вставки, front_inserter, работает специфическим образом: каждый вставляемый элемент помещается
в начале контейнера, что приводит к тому, что значения следуют в обратном порядке. Например, если заменим в предыдущем примере back_inserter на front_inserter
copy (v. begin () , v.end(), front_inserter (L)) ;
будут выведены следующие значения:
50 40 30 20 10 1 2 3 4 5
Мы также можем использовать итераторы вставки другими способами. Например, вместо
L.push_front(111);
L.push_back(999);
можно написать:
*front_inserter (L)=111; *back_inserter (L)=999;
Слайд 9Обратные итераторы
vector::iterator vector::reverse_iterator
vector::const_iterator vector::const_reverse_iterator
Обратный итератор может использоваться в следующем фрагменте
для вывода всех элементов вектора v в обратном порядке:
vector::reverse_iterator i;
for ( i=v.rbegin() ; i != v.rend() ; ++ i)
cout << * i << " ";
Типы итераторов, имена которых начинаются с const, нужны, когда контейнер сам имеет атрибут const:
Слайд 10 // const_iterator и const_reverse_iterator
#include
#include
using namespace std;
void showlist (const list
&x) { // Вперед:
list
::const_iterator i;
for (i=x.begin(); i != x.end(); ++ i)
cout << * i << " ";
cout << endl; // Вывод: 10 20 30
// Назад:
list::const_reverse_iterator j;
for ( j=x.rbegin(); j != x.rend() ; ++j)
cout << * j << " "; cout << endl; // Вывод: 30 20 10
}
Пример
Слайд 11int main() {
list L;
L.push_back(10); L.push_back(20); L.push_back(30);
showlist (L);
return 0;
}
Чтобы
удалить префикс const_, встречающийся два раза в функции showlist, мы должны также удалить ключевое слово const, имеющееся в первой строке этой функции.
Поскольку функция не модифицирует список, передаваемый ей в качестве параметра, хороший стиль программирования требует наличия const в этом примере.
Слайд 12Еще о реверсивных (обратных) итераторах
Реверсивный итератор адаптирует оператор operator-- других итераторов
таким образом, что когда используется оператор operator++, то на самом деле вызывается оператор operator--. Это позволяет перебирать элементы контейнера или последовательности в обратном порядке, не меняя код, который основывается на применении оператора operator++ .
Например, если имеется функция, которая выводит в поток элементы некоторого контейнера посредством передачи в функцию диапазона элементов с помощью двух итераторов.
То, передавая реверсивные итераторы, можно, например, выводить элементы массива целых чисел в обратном порядке.
В следующем примере адаптер итераторов std::reverse_iterator адаптирует указатели типа int * для операций с ними в обратном порядке следования элементов массива. Он использует те методы указателей, которые уже определены и существуют для указателей.
(по материалу из https://ru.stackoverflow.com/questions/479987/Что-такое-адаптер)
Слайд 13#include
#include
template
std::ostream & display ( ForwardIterator first, ForwardIterator last,
std::ostream &os = std::cout ) {
for ( ; first != last; ++first ) {
os << *first << ' ';
}
return os;
}
int main() {
const size_t N = 10;
int a[N] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
display( a, a + N ) << “ и ”;
display( std::reverse_iterator( a + N ),
std::reverse_iterator( a ) ) << std::endl;
} // Вывод : 0 1 2 3 4 5 6 7 8 9 и 9 8 7 6 5 4 3 2 1 0
Слайд 14Адаптер итератора перемещения
std::move_iterator
Этот класс адаптирует итератор так, чтобы разыменование производило ссылки
на rvalue (как если было бы применено std::move ), а остальные операции ведут себя как в обычном итераторе.
Этот адаптер итератора хранит внутреннюю копию итератора (известного как базовый итератор), на которой отражаются все операции.
Копию базового итератора с текущим состоянием можно получить в любое время, вызвав элемент base().
Слайд 15move_iterator
#include
#include
#include
#include
#include
int main
() {
std::vector foo (3);
std::vector bar {"one","two","three"};
typedef std::vector::iterator Iter;
std::copy ( std::move_iterator(bar.begin()),
std::move_iterator(bar.end()),
foo.begin() );
bar.clear();
std::cout << "foo:";
for (std::string& x : foo) std::cout << ' ' << x;
std::cout << '\n'; return 0;}
Output: foo: one two three
Слайд 16std::make_move_iterator
#include // std::cout
#include // std::make_move_iterator
#include // std::vector
#include //
std::string
#include
// std::copy
int main () {
std::vector foo (3);
std::vector bar {"one","two","three"};
std::copy ( make_move_iterator (bar.begin()),
make_move_iterator (bar.end()),
foo.begin() );
bar.clear(); // бар теперь содержит неопределённые значения;
std::cout << "foo:";
for (std::string& x : foo) std::cout << ' ' << x;
std::cout << '\n';
return 0;
}
Output: foo: one two three
Слайд 17Адаптеры потоковых итераторов
Эти адаптеры делают удобным работу с потоками ввода и вывода,
а также буферами потоков, представляя их в виде итераторов ввода и вывода: std::istream_iterator, std::ostream_iterator, std::istreambuf_iterator и std::ostreambuf_iterator.
В следующем примере показано, как тип std::istream_iterator можно использовать для чтения набора значений из стандартного потока ввода std::cin, а тип std::ostream_iterator - для записи этого набора после сортировки в строковый поток.
Слайд 18Пример
#include
#include
#include
#include
#include
#include
int _tmain(int argc, _TCHAR* argv[ ]) {
std::vector values;
std::stringstream sstm;
std::string s;
std::copy(std::istream_iterator(std::cin)
// Прочитать значения
, std::istream_iterator(), std::back_inserter(values));
std::sort(values.begin(), values.end()); // Отсортировать значения
std::copy(values.begin(), values.end() , std::ostream_iterator(sstm, " ")); // Вывод
s= sstm.str(); // передать из потока в строку
std::cout<return 0;
}
Слайд 19ostream_iterator и istream_iterator
#include
#include
#include
#include
int main
() {
std::vector myvector;
for (int i=1; i<10; ++i) myvector.push_back (i*10);
std::ostream_iterator out_it (std::cout,", ");
std::copy ( myvector.begin(), myvector.end(), out_it );
return 0;
}
Output: 10, 20, 30, 40, 50, 60, 70, 80, 90,
Слайд 20Пример istream_iterator
#include // std::cin, std::cout
#include // std::istream_iterator
int main () {
double value1, value2;
std::cout << "Please, insert two values: ";
std::istream_iterator eos; // концевой итератор
std::istream_iterator iit (std::cin); // stdin итератор
if (iit != eos) value1=*iit;
++iit;
if (iit !=eos) value2=*iit;
std::cout << value1 << "*" << value2 << "=" << (value1*value2) << '\n';
return 0;
}
Possible output:
Please, insert two values: 2 32
2*32=64
Слайд 21Чтение из файла
#include
#include
#include
#include
using namespace std;
typedef istream_iterator istream_iter;
int main() {
vector
a;
ifstream file("example.txt"); // содержимое файла: 20 30 40 50
if (file.fail()) {
cout << "Cannot open file example.txt.\n";
return 1;
}
copy(istream_iter(file), istream_iter(),
inserter(a, a.begin() )); // копируем из файла прямо в вектор
copy(a.begin(), a.end(),
ostream_iterator(cout, " ")); // копируем из вектора прямо в cout
cout << endl; return 0;
}
Слайд 22std::istreambuf_iterator
Класс istreambuf_iterator очень похож на istream_iterator. Однако вместо форматированного ввода произвольных типов
он читает одиночные символы из потока ввода.
Это адаптер итератора ввода, и, как и istream_iterator, istreambuf_iterator достигает специального значения "за последним элементом", когда доходит до конца потока.
Например.
В буфер читаются все оставшиеся символы из стандартного потока ввода.
int main() {
istreambuf_iterator first(cin);
istreambuf_iterator end_of_stream;
vector buffer (first, end_of_stream);
}
Слайд 23std:: ostreambuf_iterator
Класс ostreambuf_iterator адаптер итератора вывода, который записывает символы в поток
вывода.
В отличие от ostream_iterator, вместо обобщенного форматированного вывода произвольных типов он записывает одиночные символы с помощью функции члена sputc класса streambuf.
Например:
int main() {
string s = "This is a test\n“;
copy(s.begin(), s.end(), ostreambuf_iterator (cout) );
}
Слайд 24Пример
#include // std::cin, std::cout
#include // std::istreambuf_iterator
#include // std::string
int main
() {
std::istreambuf_iterator eos; // end-of-range iterator
std::istreambuf_iterator iit (std::cin.rdbuf()); // stdin iterator
std::string mystring;
std::cout << "Please, enter your name: ";
while (iit !=eos && *iit != '\n') mystring += *iit++;
std::cout << "Your name is " << mystring << ".\n";
return 0;
}
Possible output:
Please, enter your name: Andrey
Your name is Andrey
Слайд 25Пример
#include // std::cin, std::cout
#include // std::ostreambuf_iterator
#include // std::string
#include
// std::copy
int main () {
std::string mystring ("Some text here...\n");
std::ostreambuf_iterator out_it (std::cout); // stdout iterator
std::copy ( mystring.begin(), mystring.end(), out_it);
return 0;
}
Слайд 26Пример из книги С. Мейерса
Совет 29. Рассмотрите возможность использования istreambuf_iterator при посимвольном
вводе (“Эффективное использование STL”).
Предположим, вы хотите скопировать текстовый файл в объект string. На первый взгляд следующее решение выглядит вполне разумно:
ifstream inputfile ( "interestingData.txt");
// Прочитать inputFile в fileData
string fileData ( istream_iterator(inputFile) , istream_iterator() );
Но вскоре выясняется, что приведенный синтаксис не копирует в строку пропуски (whitespace), входящие в файл.
Это объясняется тем, что istream_iterator производит непосредственное чтение функциями operator, а эти функции по умолчанию не читают пропуски.
Слайд 27Чтобы сохранить пропуски, входящие в файл, достаточно включить режим чтения пропусков сбросом
флага skipws для входного потока:
ifstream inputfile ("interestingData.txt");
inputFile.unset(ios::skipws); // Включить режим чтения пропусков в inputFile
string fileData( istream_iterator(inputFile) , istream_iterator() );
Теперь все символы inputFile копируются в fileData.
Кроме того, может выясниться, что копирование происходит не так быстро, как хотелось бы.
Функции operator<<, от которых зависит работа istream_iterator, производят форматный ввод, а это означает, что каждый вызов сопровождается многочисленными служебными операциями.
Слайд 28Более эффективное решение основано на использовании итератора istreambuf_iterator. Итераторы istreambuf_iterator работают аналогично
istream_iterator, но если объекты istream_iterator читают отдельные символы из входного потока оператором , то объекты istreambuf_iterator обращаются прямо к буферу потока и непосредственно читают следующий символ (выражаясь точнее, объект istreambuf_iterator читает следующий символ из входного потока s вызовом
s.rdbuf ( )->sgetc( );
ifstream inputfile ( "interestingData.txt");
string fileData ( istreambuf_iterator(inputfile ),
istreambuf_iterator() );
На этот раз сбрасывать флаг skipws не нужно, итераторы istreambuf_iterator никогда не пропускают символы при вводе и просто возвращают следующий символ из буфера.
Слайд 29Мои опыты
// рекомендую со следующей программой поработать самостоятельно
// в ней разные варианты
- изучите
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
template
__int64 time_call(Function&& f) // для подсчета времени
{
__int64 begin = GetTickCount();
f();
return GetTickCount() - begin;
}
Слайд 30template< typename Fwd>
void printData (Fwd first, Fwd last, char* delim =",", ostream&
out = cout ){
while (first != last) {
out << *first;
if (++first != last)
out << delim << ' ';
}
out<}
template
void printAllData (const C& c, char* delim="," , ostream& out = cout){
printData( c.begin( ), c.end( ), delim, out);
}
Слайд 31int _tmain(int argc, _TCHAR* argv[ ]){
int i;
setlocale(LC_ALL, "ru");
cout<<"введите вариант
алгоритма"<< endl;
cin>>i;
const int COUNT =10000;
if(i==0){
ifstream inputfile ;
__int64 begin = GetTickCount();
string fileData;
for(int i=0;i< COUNT; i++){
inputfile.open ( "data.txt");
fileData = string( istream_iterator( inputfile) ,
istream_iterator() );
inputfile.close();
}
wcout<< L"serial: " << GetTickCount() - begin<< endl;;
cout<< fileData;
} // 0
Слайд 32else if(i==1){
string fileData ;
__int64 begin = GetTickCount(); // сравнение времени выполнения
for(int i=0;i< COUNT; i++){
fileData = string( istreambuf_iterator( ifstream ( "data.txt") ) ,
istreambuf_iterator() );
}
wcout<< L"serial: " << GetTickCount() - begin<< endl;
cout<< fileData;
} // 1
else if(i==2){
string fileData ;
__int64 begin = GetTickCount(); // использование умного указателя (след. сем.)
for( int i=0;i< COUNT; i++){
fileData = string( ( istreambuf_iterator( *( unique_ptr ( new
ifstream("data.txt") )) )),
istreambuf_iterator() ); // вывод
}
wcout<< L"serial: " << GetTickCount() - begin<< endl;
cout<< fileData;
} // 2
Слайд 33else if(i==3) {
string fileData ;
ifstream dataFile ( "data.txt"); // многострочное чтение
файла
typedef int T ;
istream_iterator dataBegin (dataFile);
istream_iterator dataEnd;
list data(dataBegin, dataEnd);
printAllData ( data," ");
} // 3
else if(i==4) {
// одной строкой читаем собственный файл в строку и выводим на консоль
cout<< string( ( istreambuf_iterator( *( unique_ptr
( new ifstream(__FILE__ ) )) )), // __FILE__
istreambuf_iterator() );
}
Слайд 34else if(i==5) {
// одной строкой читаем файл и сразу кладем токены
в список
list< string> data( (istream_iterator< string> ( ifstream ( "data.txt") )),
istream_iterator< string>() );
printAllData ( data," "); // и печатаем из списка
}
else {
cout<< string ( ( istreambuf_iterator( ifstream (__FILE__ ) ) ), // __FILE__
istreambuf_iterator() ); // читаем файл с помощью istreambuf_iterator
cout<< string("");
}
return 0;
}