Студопедия
Случайная страница | ТОМ-1 | ТОМ-2 | ТОМ-3
АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатика
ИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханика
ОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторика
СоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансы
ХимияЧерчениеЭкологияЭкономикаЭлектроника

Полиморфизм и виртуальные функции. Применение динамического полиморфизма. Виртуальные деструкторы. Абстрактные классы и чисто виртуальные функции.

Читайте также:
  1. XXIV. Против хулителей Пречистой Божией Матери.
  2. Абстрактные Дракон-схемы
  3. АВТОМОБИЛИ УЧАСТНИКОВ. ЗАЧЕТНЫЕ КЛАССЫ
  4. АВТОМОБИЛИ УЧАСТНИКОВ. КЛАССЫ. ОБЯЗАТЕЛЬНЫЕ НАДПИСИ.
  5. Алгоритм пособия при чисто ягодичном предложении (Цовьянов I).
  6. Андрей Применение психо-энергетических практик в ОС
  7. АСБОЦЕМЕНТНЫЕ ИЗДЕЛИЯ. ВИДЫ. ПРИМЕНЕНИЕ.

Динамический полиморфизм – выбор функции производится по используемому при выводе имени, типу вызываемого объекта и типу используемый параметров. Если объявленный тип не совпадает с типом использованного объекта, тип этого объекта идентифицируется и проверяется во время исполнения программы, что позволяет осуществить правильный выбор соответствующей функции. Это и называется динамическим полиморфизмом (поздним связыванием). Динамическое связывание приводит к небольшим издержкам памяти и времени выполнения программы, поэтому в C++ его используют только в случае явного указания, посредством ключевого слова virtual. Динамическое связывание используется путем объявления виртуальной функции. Виртуальная функция может реализовываться алгоритмами, которые, как правило, отличаются между собой. Если коды функции различны, то механизм виртуальности не включается. При использовании виртуальной функции, каждый аспект виртуальной функции, вызываемой из базового класса, определяется на этапе выполнения программы, когда известно для какого объекта вызван метод: базового класса или производного. Если вызов функции осуществляется через указатель на базовый класс, нужный аспект функции вызывается в зависимости от того, адрес объекта какого класса будет содержать указатель.

Виртуальные деструкторы

Довольно типичная ситуация, когда динамически создаётся объект производного класс, а указатель используется базового класса. При уничтожении объекта вызывается деструктор только базового класса. Например.

class Base{public: Base(){ cout << "B "; } ~Base(){ cout << "B "; }};class Derive: Base{public: Derive(){ cout << "D "; } ~Derive(){ cout << "~D "; }}; Base *p = new Derive;delete p;

Выведет: "B D ~B".

Чтобы не допускать таких ситуаций необходимо использовать виртуальный деструктор. Если при объявлении деструктора базового класса он объявляется как виртуальный, то все конструкторы производных классов также являются виртуальными. При разрушении объекта с помощью оператора delete через указатель на базовый класс будут корректно вызваны деструкторы производных классов.

class Base{public: virtual Base(){ cout << "B "; } ~Base(){ cout << "B "; }};class Derive: Base{public: Derive(){ cout << "D "; } ~Derive(){ cout << "~D "; }}; Base *p = new Derive;delete p;

Выведет: "B D ~D ~B".

Абстрактные классы используются в качестве обобщенных концепций, на основе которых можно создавать более конкретные производные классы. Невозможно создать объект типа абстрактного класса; однако можно использовать указатели и ссылки на типы абстрактного класса.

Класс, содержащий хотя бы одну чисто виртуальную функцию, считается абстрактным. Классы, производные от абстрактного класса, должны реализовать все его чисто виртуальные функции, иначе они также будут абстрактными классами.

Виртуальная функция объявляется как "чистая" с помощью синтаксиса спецификатора-чистоты (рассмотренного в разделе Реализация протокола класса). Рассмотрим пример, представленный в разделе Виртуальные функции. Класс Account создан для того, чтобы предоставлять общие функции, но объекты типа Account имеют слишком общий характер для практического применения. Таким образом, класс Account является хорошим кандидатом в абстрактный класс:

 

// deriv_AbstractClasses.cpp

// compile with: /LD

class Account {

public:

Account(double d); // Constructor.

virtual double GetBalance(); // Obtain balance.

virtual void PrintBalance() = 0; // Pure virtual function.

private:

double _balance;

};

Единственное различие между этим и предыдущим объявлениями состоит в том, что функция PrintBalance объявлена со спецификатором чисто виртуальной функции pure (= 0).

21. Полнофункционольный ввод/вывод в С++. Форматирование потока. Опции ввода/вывода в С/С++. Список классов iostream.

22. Полнофункционольный ввод/вывод в С++. Классы потокового ввода. Классы потокового вывода. Классы буферизированных потоков. Класс строковых потоков.

23. Полнофункционольный ввод/вывод в С++. Двоичные файлы. Часто применяемые функции. Объединение программ на С и С++. Использование спецификатора extern "С".

24. Полнофункционольный ввод/вывод в С++. Создание пользовательских манипуляторов. Манипуляторы без параметров. Манипуляторы с одним параметром. Манипуляторы с несколькими параметрами. (4 вопроса объединены в 1)

Полнофункциональный ввод-вывод в С++.

Особенности ввода-вывод в языке С++.
В языке С++ ввод/вывод описывается как набор классов, описанный в заголовочном файле iostream.h. Аналогами потоков stdin, stdout, stderr являются классы cin, cout и cerr. Эти три потока открываются автоматически. Поток cin связан с клавиатурой, а cout, cerr - с дисплеем.
Классы iostream используют перегруженные операции “<<” для занесения (записи) в поток и операции “>>” для извлечения (чтения) из потока. Обе операции возвращают ссылку на тип iostream, что позволяет объединять в одной строке несколько потоковых операций. Существуют перегруженные операции для всех типов данных (в том числе и для пользовательских), тем самым необходимость проверки соответствия типов отпадает. Компилятор самостоятельно выбирает нужную операцию в соответствии с типом данных:

int ivalue;
float fvalue;
char c;
cin>>ivalue>>fvalue>>c;
cout<<”integer: “<<ivalue<<endl<<” float: “<<fvalue<<”char: “<<c;

Для вставки символа перевода строки необходимо либо выводить символ ‘\n’, либо константу endl.
Для форматирования выводимых данных для объектов iostream включены следующие методы:
precision(int p) - количество знаков после запятой,
width(int w) - размер поля для вывода,
setf(long manip) - установка флагов, определяющих формат вывода(ios::dec, ios::oct, ios::hex, ios::fixed, ios::scientific, ios::left, ios::right).
Классы iostream.
Все объекты ввода/вывода, описанные в библиотеке iostream, используют оди и тот же базовый класс ios (за исключением классов буферизованных потоков). Эти производные классы делятся на 4 категории.

Классы потокового ввода
Istream Универсальный класс ввода, или родительский класс для других производных потоковых классов ввода.
Ifstream Ввод из файлов.
istream_withassign Ввод из потока cin.
Istrstream Ввод из строки.
Классы потокового вывода
Ostream Универсальный класс вывода, или родительский класс для других производных потоковых классов вывода.
Ofstream Вывод в файлы.
ostream_withassign Вывод в потоки cout, cerr и clog.
Ostrstream Вывод в строку.
Классы потокового ввода/вывода
iostream Универсальный класс ввода/вывода, или родительский класс для других производных потоковых классов ввода/вывода.
fstream Ввод/вывод в файлы.
stdiostream Стандартный поток ввода/вывода.
strstream Ввод/вывод в строку.
Классы буферизованных потоков
streambuf Родительский класс для производных буферизованных классов.
filebuf Буферизованный поток для файлов.
stdiobuf Буферизованный поток для стандартного файлового ввода/вывода.
strstreambuf Буферизованный поток для строк.

Все классы, производные от ios использут объект класса streambuf.
Потоковый ввод-вывод.
В следующем примере используется класс ifsteam:

#include <fstream.h>
void main()
{
char one_line[80];
//объявление потока, дескриптора файла

ifstream my_input_stream(“iputfile.cpp”,ios::in);
while(my_input_stream)
{
// ввод строки из потока
my_input_stream.getline(one_line,sizeof(one_line),’*’);

cout<<endl<<one_line;

}
//закрытие потока
my_input_stream.close();

}

Для объекта my_input_stream можно использовать методы open(), rdbuf() из класса ifstream и ряд методов из родительского класса istream: gcount(),get(), getline(), ignore(), peek(), putback(), read(), seekg(), tellg().
При создании объекта вызывается конструктор, которому передается имя файла, а также один или несколько режимов ввода, задаваемые константами, предопределенными в классе ios (ios::in ios::binary ios::nocreate). Константы объединяются с помощью побитовой операции ИЛИ “|”. Значение дескриптора файла можно использовать в логических проверках. При достижении конца файла значение устанавливается в ноль (условие EOF). Метод getline() читает текстовые строки из входного потока (знак “*” является разделителем строк.
Пример программы, использующией ofstream:

#include <fstream.h>
#include <string.h>
void main()
{
int i=0;
long ltellp;

char sample[40]=”Sample string\n”;
// объявление потока

ofstream my_out_stream(“outfile.out”,ios::out);
while(sample[i]!=0)
{
//вывод символа
my_out_stream.put(sample[i]);
//определение позиции вывода

ltellp=my_out_stream.tellp();
cout<<”\ntellp value: “<<ltellp;
i++;

}
my_out_stream.write(sample,strlen(sample));
ltellp.my_out_stream.tellp();
cout<<”\ntellp after write: “<<ltellp;
my_out_stream.close();

}

Обратите внимание на то, что при выполнении этой программы производится преобразование символа “\n” в два символа в файле.
Допустимо использование методов open(), rdbuf() из класса ofstream и flush(), put(), seekp(), tellp(), write() из класса ostream.
Буферизованные потоки.
Класс streambuf используется для потокового буферизованного ввода/вывода. В следующей программе описываются два элемента типа filebuf, после этого открываются текстовые файлы, Затем каждый элемент filebuf связывается с соответствующим объектом. Далее выполняется печать из входного потока в выходной.

#include <fstream.h>
#include <fcntl.h>
void main()
{
char ch;
long linecount=0;
//объявление буферов
filebuf inbuf,outbuf;
//открытие входного файла
inbuf.open(“infile.dat”,_O_RDONLY | _ O_TEXT);
if(inbuf.is_open()==0)
{
cerr<<”cant open input file”;
return;
}
//объявление входного потока
istream is(&inbuf);
//открытие выходного файла
outbuf.open(“outfile.dat”,_O_WDONLY | _ O_TEXT);
if(outbuf.is_open()==0)
{
cerr<<”cant open output file”;
return;
}
//объявление выходного потока
ostream os(&outbuf);
while(is)
{
//чтение из потока
is.get(ch);
//запись в поток

os.put(ch);
if(ch==’\n’)linecount++;

}
//закрытие файлов
inbuf.close();
outbuf.close();
cout<<”\nYou had: “<<linecount<<” lines”;

}

Допустимо использовать следующие методы класса filebuf: attach(), close(), fd(), is_open(), open(), overflow(), seekoff(), setbuf(), sync(), underflow().

Строковые потоки.
Все буферизованные объекты используют фиксированный буфер памяти. Его можно разделить на get область и put область. Они могут накладываться друг на друга. Программа может манипулировать этими областями при помощи защищенных методов класса. Для объектов, созданных на основе streambuf имеются отдельные указатели для ввода и для вывода.
Класс strsterambuf порождается из класса streambuf. Представители этого класса могут использовать следующие методы: sgets(),sgetn(), sputs(), sputn(), snextc(), sbumpc(), stossc(), sputbackc(), out_waiting(), in_avail(),dbp(), seekof(), seekpos(), overflow(), underflow(), setbuf(), sync().

#include <strstrea.h>
void main()
{
chat c;
//объявление потока

strstreambuf mybuf(1024);
//запись в поток
mubuf.sputc(‘A’);
//чтение из потока
c=mybuf.sgetc();
cout<<c;

}

Двоичные файлы.
Двоичные файлы или потоки содержат последовательности байтов; никаких преобразований символов не выполняется. Для указания двоичного режима в конструктор необходимо добавить описатель ios::binary:

#include <fstream.h>
#include <string.h>
void main()
{
int i=0;
long ltellp;

char sample[40]=”Sample string\n”;
//объявление двоичного потока

ofstream my_out_stream(“outfile.out”,ios::out | ios::binary);
while(sample[i]!=0)
{
//запись одиночного байта в двоичный поток
my_out_stream.put(sample[i]);
//определение позиции вывода

ltellp=my_out_stream.tellp();
cout<<”\ntellp value: “<<ltellp;
i++;

}
//запись блока в поток
my_out_stream.write(sample,strlen(sample));
ltellp.my_out_stream.tellp();
cout<<”\ntellp after write: “<<ltellp;
my_out_stream.close();

}

ПОДРОБНОЕ РАССМОТРЕНИЕ! (ПРЕДУПРЖДЕНИЕ – МНОГО БУКОВ)

Частью стандартной библиотеки C++ является библиотека iostream – объектно-ориентированная иерархия классов, где используется и множественное, и виртуальное наследование. В ней реализована поддержка для файлового ввода/вывода данных встроенных типов. Кроме того, разработчики классов могут расширять эту библиотеку для чтения и записи новых типов данных.

Для использования библиотеки iostream в программе необходимо включить заголовочный файл

#include <iostream>

Операции ввода/вывода выполняются с помощью классов istream (потоковый ввод) и ostream (потоковый вывод). Третий класс, iostream, является производным от них и поддерживает двунаправленный ввод/вывод. Для удобства в библиотеке определены три стандартных объекта-потока:

Вывод осуществляется, как правило, с помощью перегруженного оператора сдвига влево (<<), а ввод – с помощью оператора сдвига вправо (>>):

#include <iostream>

#include <string>

 

int main()

{

string in_string;

 

// вывести литерал на терминал пользователя

cout << "Введите свое имя, пожалуйста: ";

 

// прочитать ответ пользователя в in_string

cin >> in_string;

 

if (in_string.empty())

// вывести сообщение об ошибке на терминал пользователя

cerr << "ошибка: введенная строка пуста!\n";

else cout << "Привет, " << in_string << "!\n";

}

Назначение операторов легче запомнить, если считать, что каждый "указывает" в сторону перемещения данных. Например,

>> x

перемещает данные в x, а

<< x

перемещает данные из x.

Помимо чтения с терминала и записи на него, библиотека iostream поддерживает чтение и запись в файлы. Для этого предназначены следующие классы:

Чтобы использовать часть библиотеки iostream, связанную с файловым вводом/выводом, необходимо включить в программу заголовочный файл

#include <fstream>

(Файл fstream уже включает iostream, так что включать оба файла необязательно.) Файловый ввод/вывод поддерживается теми же операторами:

#include <fstream>

#include <string>

#include <vector>

#include <algorithm>

int main()

{

string ifile;

 

cout <"Введите имя файла для сортировки: ";

cin >> ifile;

 

// сконструировать объект класса ifstream для ввода из файла

ifstream infile(ifile.c_str());

 

if (! infile) {

cerr < "ошибка: не могу открыть входной файл: "

< ifile < endl;

return -1;

}

 

string ofile = ifile + ".sort";

 

// сконструировать объект класса ofstream для вывода в файл

ofstream outfile(ofile.c_str());

if (! outfile) {

cerr <<"ошибка: не могу открыть выходной файл: "

<< ofile <<endl;

return -2;

}

 

string buffer;

vector< string, allocator > text;

 

int cnt = 1;

while (infile >> buffer) {

text.push_back(buffer);

cout <<buffer << (cnt++ % 8? " ": "\n");

}

 

sort(text.begin(), text.end());

 

// выводим отсортированное множество слов в файл

vector<string >::iterator iter = text.begin();

for (cnt = 1; iter!= text.end(); ++iter, ++cnt)

outfile << *iter

<< (cnt % 8? " ": "\n");

 

return 0;

}

Вот пример сеанса работы с этой программой. Нас просят ввести файл для сортировки. Мы набираем alice_emma (набранные на клавиатуре символы напечатаны полужирным шрифтом). Затем программа направляет на стандартный вывод все, что прочитала из файла:

Введите имя файла для сортировки: alice_emma

Alice Emma has long flowing red hair. Her

Daddy says when the wind blows through her

hair, it looks almost alive, like a fiery

bird in flight. A beautiful fiery bird, he

tells her, magical but untamed. "Daddy, shush, there

is no such creature," she tells him, at

the same time wanting him to tell her

more. Shyly, she asks, "I mean, Daddy, is

there?"

Далее программа выводит в файл outfile отсортированную последовательность строк. Конечно, на порядок слов влияют знаки препинания; в следующем разделе мы это исправим:

"Daddy, "I A Alice Daddy Daddy, Emma Her

Shyly, a alive, almost asks, at beautiful bird

bird, blows but creature," fiery fiery flight. flowing

hair, hair. has he her her her, him

him, in is is it like long looks

magical mean, more. no red same says she

she shush, such tell tells tells the the

there there?" through time to untamed. wanting when

wind

Библиотека iostream поддерживает также ввод/вывод в область памяти, при этом поток связывается со строкой в памяти программы. С помощью потоковых операторов ввода/вывода мы можем записывать данные в эту строку и читать их оттуда. Объект для строкового ввода/вывода определяется как экземпляр одного из следующих классов:

Для использования любого из этих классов в программу нужно включить заголовочный файл

#include <sstream>

(Файл sstream уже включает iostream, так что включать оба файла необязательно.) В следующем фрагменте объект класса ostringstream используется для форматирования сообщения об ошибке, которое возвращается вызывающей программе.

#include <sstream>string program_name("our_program");string version(0.01);//... string mumble(int *array, int size){ if (! array) { ostringstream out_message; out_message << "ошибка: " << program_name << "--" << version << ": " << __FILE__ << ": " << __LINE__ << " -- указатель равен 0; " << " а должен адресовать массив.\n"; // возвращаем строку, в которой находится сообщение return out_message.str(); } //...}

Потоки ввода/вывода поддерживают два предопределенных типа: char и wchar_t. В этой главе мы расскажем только о чтении и записи в потоки данных типа char. Помимо них, в библиотеке iostream имеется набор классов и объектов для работы с типом wchar_t. Они отличаются от соответствующих классов, использующих тип char, наличием префикса ‘w’. Так, объект стандартного ввода называется wcin, стандартного вывода – wcout, стандартного вывода для ошибок – wcerr. Но набор заголовочных файлов для char и wchar_t один и тот же.

Классы для ввода/вывода данных типа wchar_t называются wostream, wistream, wiostream, для файлового ввода/вывода – wofstream, wifstream, wfstream, а для строкового – wostringstream, wistringstream, wstringstream.


Дата добавления: 2015-09-03; просмотров: 139 | Нарушение авторских прав


Читайте в этой же книге: Неполный конструктор | Перезагрузка конструкторов | Перегрузка операций (операторов). Понятие перегрузки операторов. Синтаксис перегрузки операции. Перегрузка бинарных операторов. | Перегрузка операций (операторов). Перегрузка операторов отношения и логических операторов. Перегрузка оператора присваивания. Перегрузка унарных операторов. | Перегрузка операций (операторов). Перегрузка операторов инкремента и декремента. Перегрузка оператора индексирования. Перегрузка оператора вызова функции. | Перегрузка операций (операторов). Перегрузка операторов доступа к членам класса. Перегрузка операторов new и delete. Функции преобразования типа. | Самое длинное слово beautiful | Строковые потоки | Состояние формата (ОТНОСИТСЯ К ПОСЛЕДНЕМУ ВОПРОСУ, КОТОРЫЙ 24) | Работа с несовместимыми конструкциями. |
<== предыдущая страница | следующая страница ==>
Полиморфизм и виртуальные функции. Раннее и позднее связывание. Динамический полиморфизм. Виртуальные функции. Виртуальные и невиртуальные функции.| Длина самого длинного слова 10

mybiblioteka.su - 2015-2024 год. (0.03 сек.)