Достоинства и недостатки шаблонов — КиберПедия 

Типы оградительных сооружений в морском порту: По расположению оградительных сооружений в плане различают волноломы, обе оконечности...

Состав сооружений: решетки и песколовки: Решетки – это первое устройство в схеме очистных сооружений. Они представляют...

Достоинства и недостатки шаблонов

2017-10-21 564
Достоинства и недостатки шаблонов 0.00 из 5.00 0 оценок
Заказать работу

Шаблоны представляют собой мощное и эффективное средство обращения с различными типами данных, которое можно назвать параметрическим полиморфизмом, обеспечивают безопасное использование типов, в отличие от макросов препроцессора, и являются вкупе с шаблонами функций средством реализации идей обобщенного программирования и метапрограммирования. Однако следует иметь в виду, что эти средства предназначены для грамотного использования и требуют знания многих тонкостей. Программа, использующая шаблоны, содержит код для каждого порожденного типа, что может увеличить размер исполняемого файла. Кроме того, с одними типами данных шаблоны могут работать не так эффективно, как с другими. В этом случае имеет смысл использовать специализацию шаблона (о специализации шаблонов можно прочитать в учебнике [18]).

Стандартная библиотека С++ предоставляет большой набор шаблонов для различных способов организации хранения и обработки данных.


 

8 Практические примеры. Файловые потоки. Пользовательские функции

Будем рассматривать работу с текстовыми файлами (*.txt) и таблицами MS Excel (*.xls).

К примеру, требуется составить программу для решения дифференциального уравнения. Программа выдаст в качестве ответа набор чисел. Количество чисел может быть любым числом – и 10, и 200. Поэтому разумно в этом случае печатать эти данные не столько на экран, сколько в файл *.xls, а затем в MS Excel строить график. При этом говорят, что программа выводит в файл данные.

Приведем другой пример. Имеется файл, который содержит какие-то данные. Скажем, нужно их рассчитать по какой-то формуле. Тогда программа должна считать из файла данные.

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

Для работы с файловым потоком необходимо:

1. подключить библиотеку fstream:

#include <fstream>

2. объявить переменную типа файловый поток:

fstream f;

3. открыть файл:

o для записи в файл:

f.open("1.txt", ios::out);

o для чтения из файла:

f.open("1.txt", ios::in);

4. произвести запись в файл или чтение из файла:

o для записи в файл:

f<<"x="<<x;

o для чтения из файла:

f>>x;

5. закрыть файл: f.close();

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

f.open("1.txt", ios::app);

Пример 1. Считать из файла число и показать его на экране.

Решение. Сначала рассмотрим, как выполнить эту задачу в Visual Studio, затем – в Borland C++.

Создадим проект в Visual Studio, назовем его "8.1". При этом генерируется папка с нашим проектом. Открываем папку 8.1\8.1 и создаем текстовый документ, называем его 1.txt, как показано на рис. 7.1.


Рис. 7.1. Создание текстового файла в папке с проектом

Открываем файл 1.txt и записываем одно число, например, -3.27, как показано на рис. 7.2.


Рис. 7.2. Исходный текстовый файл для примера 1

Сохраняем и закрываем файл. Теперь переходим в приложение Visual Studio и в нашем проекте прописываем код программы.

Код программы для примера 1:

// 8.1.cpp: определяет точку входа для консольного приложения.//#include "stdafx.h"#include <iostream>#include <fstream>using namespace std;int main(){ double x; fstream f; f.open("1.txt", ios::in); f>>x; f.close(); cout<<"x="<<x<<endl; return 0;}

Результат выполнения программы:

 

Чтобы работать с файлами в Borland C++, создадим папку F:/BC/FILES. Эта папка предназначена для хранения файлов, с которыми будет взаимодействовать наша программа. Создаем файл 1.txt в папке F:/BC/FILES, записываем в него число -3.27.

При открытии файла необходимо записать имя файла как FILES/1.txt, чтобы наша программа искала текстовый файл именно в этой папке.

Код программы:

#include <iostream.h>#include <fstream.h>int main(){ double x; fstream f; f.open("FILES/1.txt", ios::in); f>>x; f.close(); cout<<"x="<<x<<endl; return 0;}

Результат выполнения программы:

 

Пример 2. Составить программу, которая производит запись двух чисел в файлы *.txt и *.xls.

Решение. Обозначим числа как и , зададим . Файлы не будем предварительно создавать, т.к. они возникнут автоматически при запуске нашей программы. Создаем новый проект 8.2 в Visual Studio.

Объявим два файловых потока ftxt – для записи в текстовый файл 1.txt, fxls – для записи в табличный файл 2.xls.

Сделаем так, чтобы в файле 1.txt появилась надпись:

a=7.2 b=-10.89

В файле 2.xls выведем каждый кусочек сообщения в разные ячейки. Для этого следует использовать символ табуляции "\t".

Код программы для примера 2:

// 8.2.cpp: определяет точку входа для консольного приложения.//#include "stdafx.h"#include <iostream>#include <fstream>using namespace std;int main(){ double a, b; fstream ftxt, fxls; a=7.2; b=-10.89; cout<<"a="<<a<<" b="<<b<<endl; ftxt.open("1.txt", ios::out); fxls.open("2.xls", ios::out); ftxt<<"a="<<a<<" b="<<b; fxls<<"a=\t"<<a<<"\tb=\t"<<b; ftxt.close(); fxls.close(); return 0;}

Результат выполнения программы:

экран:

 

текстовый файл 1.txt:

 

табличный файл 2.xls:

 

Примечание. Файлы 1.txt и 2.xls создаются в папке с проектом 8.2\8.2, как показано на рис. 7.3.


Рис. 7.6. Созданные программой файлы в папке с проектом

Решим данную задачу в Borland C++.

Код программы:

#include <iostream.h>#include <fstream.h>int main(){ double a, b; fstream ftxt, fxls; a=7.2; b=-10.89; cout<<"a="<<a<<" b="<<b<<endl; ftxt.open("FILES/1.txt", ios::out); fxls.open("FILES/2.xls", ios::out); ftxt<<"a="<<a<<" b="<<b; fxls<<"a=\t"<<a<<"\tb=\t"<<b; ftxt.close(); fxls.close(); return 0;}

Результат выполнения программы:

экран:

 

текстовый файл 1.txt:

 

табличный файл 2.xls:

 

Примечание. Т.к. в примере 2 мы задали имя текстового файла 1.txt так же, как в примере 1, то после выполнения программы примера 2 в папке FILES будет находиться два файла. Файл 1.txt запишется заново. При этом потеряется число -3.27, которое мы печатали для примера 1.

В следующих примерах будем приводить коды программ в среде Visual Studio.

Пример 3. Построить таблицу значений функции при с шагом 0,1.

Решение. Данный пример решается с помощью циклического алгоритма. Будем использовать цикл for. Построим таблицу значений функции на экране, а также в файле 1.xls и построим график в MS Excel.

Блок-схема:

 

Код программы:

// 8.3.cpp: определяет точку входа для консольного приложения.//#include "stdafx.h"#include <iostream>#include <math.h>#include <iomanip>#include <fstream>using namespace std;int main(){ double x, y; fstream f; f.open("1.xls", ios::out); cout<<setw(10)<<"x"<<setw(10)<<"y"<<endl; f<<"x"<<"\t"<<"y"<<endl; for(x=0; x<=2; x=x+0.1){ y=sin(x); cout<<setw(10)<<x<<setw(10)<<y<<endl; f<<x<<"\t"<<y<<endl; } f.close(); return 0;}

Результат выполнения программы:

экран:

 

табличный файл 1.xls:

 

Файл 1.xls с построенным графиком:

 

Пример 4. В матрице вычислить сумму положительных элементов, произведение элементов на главной диагонали, количество элементов, больших 2.

Решение. Матрица а является двумерным массивом, состоящим из пяти строк и пяти столбцов. Проинициализируем массив по формуле . Массив и все результаты вычислений будем выводить в файл 1.xls.

Блок-схема:

 

Код программы:

// 8.4.cpp: определяет точку входа для консольного приложения.//#include "stdafx.h"#include <iostream>#include <math.h>#include <iomanip>#include <fstream>using namespace std;int main(){ double a[5][5], s, p; int i, j, k; fstream f; f.open("1.xls", ios::out); cout<<"matrix:"<<endl; f<<"матрица:"<<endl; for(i=0; i<5; i=i+1){ for(j=0; j<5; j=j+1){ a[i][j]=7.0*sin(2.3*i*j); cout<<setw(10)<<a[i][j]; f<<'\t'<<a[i][j]; } cout<<endl; f<<endl; } s=0; for(i=0; i<5; i=i+1){ for(j=0; j<5; j=j+1){ if(a[i][j]>0){ s=s+a[i][j]; } } } cout<<"s="<<s<<endl; f<<"Сумма положительных=\t"<<s<<endl; p=1; for(i=0; i<5; i=i+1){ for(j=0; j<5; j=j+1){ if(i==j){ p=p*a[i][j]; } } } cout<<"p="<<p<<endl; f<<"Произв. на гл. диаг.=\t"<<p<<endl; k=0; for(i=0; i<5; i=i+1){ for(j=0; j<5; j=j+1){ if(a[i][j]>2){ k=k+1; } } } cout<<"k="<<k<<endl; f<<"Количество элем.>2 =\t"<<k<<endl; f.close(); return 0;}

Результат выполнения программы:

экран:

 

табличный файл 1.xls:

Пользовательские функции

Ранее в программах мы использовали функции из разных библиотек, например, из библиотеки <math.h> или setw(10) из библиотеки <iomanip>. Что общего во всех функциях? Все функции представляют собой некий шаблон, по которому вычисляется значение какой-то функции или выполняется набор каких-то действий. В языке С++ есть возможность создавать свои функции, не относящиеся к библиотекам. Такие функции называются пользовательскими функциями.

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

1. объявить пользовательскую функцию:

тип_данных имя_функции (список параметров);

здесь тип_данных – это возвращаемый тип данных функции, который определяется ответом функции (выходные данные), имя функции – любое имя (по аналогии с именем переменной), список параметров – это то, с чем наша функция будет работать (входные данные);

2. описать пользовательскую функцию;

3. вызвать пользовательскую функцию.

Объявление пользовательской фукнции должно быть обязательно до функции main(), описание может быть вместе с объявлением, а может быть после функции main(). Вызов пользовательской функции может быть внутри функции main() или внутри другой пользовательской функции.

Первый вариант описания пользовательской функции – до функции main() представлен на рис. 7.4.


Рис. 7.4. Описание пользовательской функции до функции main()

Второй вариант описания пользовательской функции – после функции main() представлен на рис. 7.5.


Рис. 7.5. Описание пользовательской функции после функции main()

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

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


Рис. 7.6. Принцип работы пользовательской функции

Например, для известной нам функции из библиотеки <math.h>, входными данными является один параметр – вещественное число , сам аппарат представляет собой процесс вычисления синуса от , результатом является полученное значение синуса. Результат является вещественным числом, которое задается ключевым словом double. Поэтому можно было бы представить объявление и описание функции следующим образом:

double sin(double x){double res;res=...; // вычисление синуса по сложной математической формулеreturn res; // ответ, результат}

Здесь внутри функции объявлена локальная переменная double res, в которую будет насчитываться ответ (res – result – результат). Данная переменная будет видна только внутри пользовательской функции. Переменная res – это, своего рода, временная переменная для расчета синуса. Строка return res; выдает в ответ вычисленное значение синуса.

Вызов функции внутри функции main() выглядит следующим образом:

z=sin(1.5);

Примечание. Хотелось бы обратить внимание на разницу между списком параметров при объявлении функции и список аргументов при вызове функции. Объявление, описание и список формальных параметров составляют весь шаблон. Здесь параметры представляют собой некие абстрактные параметры, которые пока нельзя "пощупать". А вот когда идет вызов функции, абстрактные параметры приобретают вполне реальные формы и становятся аргументами функции, конкретными числами. В программе это отражается следущим образом. Объявление и описание функции делается один раз:

double function(double a, double b){...}

Вызовов функции может быть сколько угодно:

int main(){double x, y, z1, z2, z3;x=10;y=-0.3;z1=function(x,y); // первый вариант вызоваz2=function(1.8, y); // второй вариант вызоваz3=function(1.8, 0); // третий вариант вызоваreturn 0;}

Рассмотрим несколько примеров по созданию пользовательских функций.

Пример 1. Требуется создать пользовательскую функцию для вычисления квадрата вещественного числа.

Решение. Составим схему работы нашей пользовательской функции в общем виде на рис. 7.7.


Рис. 7.7. Схема в общем виде для примера 1

Т.к. параметр х должен быть вещественным, то ему соответствует тип double. Пользовательская функция должна считать квадрат числа, поэтому назовем ее kv. В ответ будет выдаваться вещественное число, поэтому возвращаемый тип у функции будет double.

Объявление и описание функции kv:

double kv(double x){double res;res=x*x;return res;}

Код программы и результат выполнения программы:

 

Пример 2. Вычислить таблицу " " значений функции при с шагом 1.

Решение. Сделаем пользовательскую функцию для вычисления значения функции . Возвращаемый тип – double, т.к. ответом является вещественное число. Параметр один, double x. В описании пользовательской функции будет вычисление функции по разветвляющемуся алгоритму. В функции main() реализуем циклический алгоритм для вывода таблицы значений функции.

Код программы:

// test.cpp: определяет точку входа для консольного приложения.//#include "stdafx.h"#include <iostream>#include <iomanip>using namespace std;double fz(double x){ double res; if(x<1){ res=x+3.0; } else { res=4.0*x; } return res;}int main(){ double x, z; cout<<setw(10)<<"x"<<setw(10)<<"z"<<endl; for(x=-1; x<=2; x=x+1){ z=fz(x); cout<<setw(10)<<x<<setw(10)<<z<<endl; }return 0;}

Результат выполнения программы:

 

Пример 3. Вычислить таблицу " " значений функции при с шагом 1, с шагом 5.

Решение. Сделаем пользовательскую функцию для вычисления значения функции . Возвращаемый тип – double, т.к. ответом является вещественное число. Параметров два, double x, double y. В описании пользовательской функции будет вычисление функции по разветвляющемуся алгоритму. В функции main() реализуем вложенные циклы для вывода таблицы значений функции.

Код программы:

// test.cpp: определяет точку входа для консольного приложения.//#include "stdafx.h"#include <iostream>#include <iomanip>using namespace std;double fz(double x, double y){ double res; if(x*y<1){ res=x+y; } else { res=x-y; } return res;}int main(){ double x, y, z; cout<<setw(10)<<"x"<<setw(10)<<"y"<<setw(10)<<"z"<<endl; for(x=-1; x<=0.5; x=x+1){ for(y=5; y<=15; y=y+5){ z=fz(x, y); cout<<setw(10)<<x<<setw(10)<<y<<setw(10)<<z<<endl; } } return 0;}

Результат выполнения программы:

 

Пример 4. Вычислить таблицу значений функции при с шагом 1.

Решение. Сделаем пользовательскую функцию для вычисления значения функции . Возвращаемый тип – double, т.к. ответом является вещественное число. Параметр один, int n. В описании пользовательской функции будет вычисление функции по циклическому алгоритму. В функции main() реализуем цикл по для вывода таблицы значений функции.

Код программы:

// test.cpp: определяет точку входа для консольного приложения.//#include "stdafx.h"#include <iostream>#include <iomanip>using namespace std;double fz(int n){ double res; int i; res=0; for(i=1; i<=n; i=i+1){ res=res+1.0/i; } return res;}int main(){ int n; double z; cout<<setw(10)<<"n"<<setw(10)<<"z"<<endl; for(n=1; n<=10; n=n+1){ z=fz(n); cout<<setw(10)<<n<<setw(10)<<z<<endl; } return 0;}

Результат выполнения программы:

 

Пример 5. Создать пользовательскую функцию для вывода массива из 5 строк и 5 столбцов на экран.

Решение. Пользовательскую функцию назовем print_mass. В качестве параметра будет сам массив, например, double х[5][5]. Результатом пользовательской функции является вывод массива на экран, поэтому никакого числа в ответе не будет. Следовательно, возвращаемый тип на этот раз будет void, что означает "пустой". В описании пользовательской функции организуем вывод двумерного массива во вложенном цикле. В функции main() инициализируем два массива и вызовем функцию print_mass два раза.

Код программы:

// test.cpp: определяет точку входа для консольного приложения.//#include "stdafx.h"#include <iostream>#include <iomanip>using namespace std;void print_mass(double x[5][5]){ int i, j; for(i=0; i<5; i=i+1){ for(j=0; j<5; j=j+1){ cout<<setw(10)<<x[i][j]; } cout<<endl; }}int main(){ double A[5][5], B[5][5]; int i,j; for(i=0; i<5; i=i+1){ for(j=0; j<5; j=j+1){ A[i][j]=2.3*i+j; B[i][j]=i-1.5*j; } } cout<<"massiv A:"<<endl; print_mass(A); cout<<"massiv B:"<<endl; print_mass(B); return 0;}

Результат выполнения программы:

 

Пример 6. Массив задан по формуле , массив по формуле . Вычислить сумму положительных элементов массивов.

Решение. Создадим пользовательскую функцию для вычисления суммы положительных элементов массива, назовем ее sum_pol. В качестве параметра будет формальный массив . В функции main() проинициализируем массивы и , затем два раза вызовем функцию sum_pol.

Код программы:

// test.cpp: определяет точку входа для консольного приложения.//#include "stdafx.h"#include <iostream>#include <iomanip>using namespace std;double sum_pol(double x[5][5]){ int i, j; double s; s=0; for(i=0; i<5; i=i+1){ for(j=0; j<5; j=j+1){ if(x[i][j]>0){ s=s+x[i][j];} } } return s;}int main(){ double A[5][5], B[5][5], sa, sb; int i,j; for(i=0; i<5; i=i+1){ for(j=0; j<5; j=j+1){ A[i][j]=0.3*i+j; B[i][j]=i-1.5*j; } } sa=sum_pol(A); cout<<"s A>0 = "<<sa<<endl; sb=sum_pol(B); cout<<"s B>0 = "<<sb<<endl; return 0;}

Результат выполнения программы:

 

Краткие итоги

Чтобы работать с файлами, необходимо использовать переменную типа fstream. Рассмотрены случаи вывода в файл и чтения из файла.

Вопросы

1. Что такое файловый поток?

2. Что необходимо сделать, чтобы использовать в программе файловый поток?

3. Как сделать разделение по ячейкам при записи в файл *.xls?

4. Что такое пользовательская функция?

5. Что такое объявление, описание, вызов пользовательской функции?

Упражнения

1. Составьте программу, которая создаст следующие файлы:

 

2. Составьте блок-схему и программу для построения таблицы значений функции при с шагом 0 с шагом 0,8. Таблицы вывести на экран, в файлы *.txt, *.xls.

3. Для массива найти минимальный элемент, максимальный элемент; найденные минимальный и максимальный элементы поменять местами. Исходный массив считать из файла. Исходный массив, результаты вычислений и измененный массив вывести на экран и в файл *.xls.

4. Составить блок-схему и программу для вычисления суммы 20 чисел из файла. Указание: использовать циклический алгоритм, не использовать массивы.

5. В программе для задачи 3 создать пользовательские функции для вычисления минимального и максимального элементов.

6. Массив считать из файла. Вывести массив на экран. Поменять местами первый и последний элементы. Измененный массив вывести на экран. Указание: для вывода массива на экран создать пользовательскую функцию.

 


Поделиться с друзьями:

Организация стока поверхностных вод: Наибольшее количество влаги на земном шаре испаряется с поверхности морей и океанов (88‰)...

Своеобразие русской архитектуры: Основной материал – дерево – быстрота постройки, но недолговечность и необходимость деления...

Семя – орган полового размножения и расселения растений: наружи у семян имеется плотный покров – кожура...

Индивидуальные очистные сооружения: К классу индивидуальных очистных сооружений относят сооружения, пропускная способность которых...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.105 с.