hkjCJgcQqF
.pdfфункцию, определяющую |
число комитетов из k членов, которые можно |
сформировать из n человек: |
|
int comm (int n, int k) |
{ |
//условие останова : слишком мало народа if (k > n)
return 0;
//условие останова : в комитете все или никого else if (n == k || k == 0)
return 1; else
//шаг рекурсии: все комитеты без персоны A
//плюс все комитеты с персоной A
return comm(n-1,k) + comm(n-1,k-1);
}
Задание
1.Создайте программу, в которой для функции вычисления факториала требуется написать объявление указателя на нее. Вызовите функцию через этот указатель для вычисления трех введенных чисел.
Вот функции из библиотеки С, определенные в заголовочном файле <cmath> :
double sin(double); double cos(double); double sqrt(double);
Объявите массив указателей на С-функции и инициализируйте его этими функциями. Напишите main(), которая вызывает sqrt() с аргументами 97.9 через элемент массива.
2. Создайте приложение, в котором определены перегруженные варианты функции error() , чтобы были корректны следующие вызовы:
int undex:
int upperBound; char selectVal; // …
error( “Массив вышел за границы: “, index, upperBound); error( “Деление на нуль” );
error(“Ошибочное выделение”, selectVal);
3.Создайте приложение, в котором имеются следующие описания:
•функция с параметрами типа указатель на символ и ссылка на целое, возвращающая значения (void);
•указатель на такую функцию;
•функция с параметром, имеющим тип такого указателя;
•функция, возвращающая такой указатель.
Напишите определение функции, у которой параметр и возвращаемое значение имеют тип такого указателя. Указание : используйте typedef.
53
4. Напишите программу, в которой пользователь вводит число кандидатов и число человек в комитете. На выходе выдается число возможных вариантов комитета.
Контрольные вопросы
1.Зачем может понадобиться объявлять перегруженные функции?
2.Объясните, к какому эффекту приводит второе объявление в каждом из приведенных примеров:
(a)int calc(int, int);
int calc(const int, const int);
(b) int get(); double get();
(c) int * reset ( int *); double * reset ( double *);
(d) extern “C” int compute(int *, int);
extern “C” double compute(double *, double); Ответ подкрепите примером программы
3.Какая из следующих инициализаций приводит к ошибке? Почему?
(a)void reset(int) ;
void (*pf) ( )=reset;
(b) int calc(int, int);
int (* pf1) (int, int)=calc;
(c) extern “C” int compute(int , int); int (*pf3) (int *, int)=compute;
Ответ подкрепите примером программы 4. Каков ранг каждого из преобразований аргументов в следующих вызовах функций:
(a ) void print(int *, int); int arr[7]; print(arr, 7);
(b)void myfunc (int, int); myfunc (‘a’, ‘z’);
(c)int calc (int, int); double dobj=1.7;
double j=calc(55.4, dobj)
(d)void set (const int *);
int *pi; set(pi);
Ответ подкрепите примером программы
(e) int calc(int, int);
int (* pf1) (int, int)=calc;
(f)extern “C” int compute(int , int); int (*pf3) (int *, int)=compute;
54
5. Объясните, что происходит при разрешении перегрузки для вызова функции compute() внутри main()
•namespace primerLib { void compute( );
void compute( const void *); } using primerLib::compute; void compute(int);
void compute(double, double=3.4); void compute(char *,char *= 0);
int main() { compute(0); return 0; }
•Какие функции являются кандидатами?
•Какие из них попадут в список подходящих после первого шага?
•Какие последовательности преобразований надо применить к фактическому аргументу, чтобы он соответствовал формальному параметру для каждой подходящей функции?
•Какая функция будет наиболее подходящей?
•Что будет, если using-объявление поместить внутрь функции main() перед вызовом compute()? Ответьте на те же вопросы.
Задание для самостоятельной работы
1. Возведение числа n в степень p -это умножение числа n на себя p раз. Напишите функцию power(), которая в качестве аргументов принимает значение типа double для n и значение типа int для p и возвращает значение типа double. Для аргумента, соответствующего степени числа, задайте значение по умолчанию, равное 2, чтобы при отсутствии показателя степени при вызове функции число n возводилось в квадрат. Затем создайте перегруженные функции , принимающие в качестве аргумента значения типа char, int, long, float. Напишите программу , вызывающую функцию power() со всеми возможными типами аргументов.
2.Напишите функцию с именем swap(), обменивающую значениями два своих аргумента типа int (обратите внимание, что изменяться должны значения переменных из вызывающей программы , а не локальных переменных функции). Выберите способ передачи аргументов. Напишите функцию(main()), использующую данную функцию.
3.Напишите программу , в которой фактический параметр не может инициализировать параметр-ссылку.
55
Глава 3. Перегрузка операторов и преобразования, определенные пользователем
Процесс, называемый перегрузка оператора, переопределяет стандартные символы операторов для реализации операций для типа класса, т.е. операторы языка, такие как +, !=, [] и = могут быть переопределены для типа класса. Перегрузка операторов позволяет вводить собственные версии предопределенных операторов для операндов типа классов.
Например, в классе MyString определяется строковый тип. Объекты используют динамическую память для сохранения строк переменной длины и перегруженные операторы для создания строковых выражений.
Спецификация класса MyString
#include <iostream> #include <string.h> #include <stdlib.h> using namespace std; #ifndef NULL const int NULL = 0; #endif // NULL
const int outOfMemory = 0, indexError = 1; class MyString
{
private:
//указатель на динамически создаваемую строку
//длина строки включает NULL символ
char *str; int size;
// функция сообщения об ошибках
void Error(int errorType, int badIndex = 0) const; public:
// конструкторы
MyString(char *s = ""); MyString(const MyString& s);
// деструктор
~MyString( );
// операторы присваивания MyString = MyString, MyString = C++String
MyString& operator= (const MyString& s); MyString& operator= (char *s);
//операторы отношений MyString==MyString, MyString==C++String,
//C++String==MyString ; operator возвращает целочисленное значение ,
//как результат сравнения
int operator== (const MyString& s) const; int operator== (char *s) const;
56
friend int operator== (char *str, const MyString& s);
//MyString!=MyString,MyString!=C++String,C++String!=MyString int operator!= (const MyString& s) const;
int operator!= (char *s) const;
friend int operator!= (char *str, const MyString& s);
//MyString<MyString ,MyString<C++String,C++String<MyString int operator< (const MyString& s) const;
int operator< (char *s) const;
friend int operator< (char *str, const MyString& s);
//MyString<=MyString,MyString<=C++String,C++String<=MyString int operator<= (const String& s) const;
int operator<= (char *s) const;
friend int operator<= (char *str, const String& s);
// String>String,String>C++String,C++String>String int operator> (const String& s) const;
int operator> (char *s) const;
friend int operator> (char *str, const String& s);
// String>=String,String>=C++String,C++String>=String int operator>= (const String& s) const;
int operator>= (char *s) const;
friend int operator>= (char *str, const String& s);
//операторы String-конкатенации MyString+MyString, MyString+C++St, C++St+MyString , MyString +=MyString, MyString += C++St
MyString operator+ (const MyString& s) const; MyString operator+ (char *s) const;
friend MyString operator+ (char *str,const MyString& s); void operator+= (const MyString& s);
void operator+= (char *s);
//MyString -функции
//начиная с первого символа , найти положение символа c int Find(char c, int start) const;
//найти последнее вхождение символа c
int FindLast(char c) const; // выделение подстроки
MyString Substr(int index, int count) const;
//вставить объект MyString в объект MyString void Insert(const MyString& s, int index);
//вставить строку типа C++String в строку типа MyString void Insert(char *s, int index);
//удалить подстроку
void Remove(int index, int count);
57
// String -индексация
char& operator[] (int n);
//преобразовать String в C++String operator char* ( ) const;
//String I/O
friend ostream& operator<< (ostream& ostr, const MyString& s); friend istream& operator>> (istream& istr, MyString& s);
// читать символы до разделителя
int ReadString(istream& is=cin, char delimiter='\n');
// дополнительные методы int Length( ) const; int IsEmpty( ) const; void Clear( );
}; Строковые переменные С++ являются массивами символов с нулевым
символом в конце. В классе MyString определяется строковый тип. Объекты используют динамическую память для сохранения строк переменной длины и перегруженные операторы для создания строковых выражений.
Объекты класса MyString могут взаимодействовать со строками С++ (char*). Класс MyString имеет деструктор, конструктор и два перегруженных оператора присваивания, позволяющие присваивать объект типа MyString или строку С++ новому объекту MyString.
Сначала определим закрытую функцию Error.
void MyString::Error(int errorType, int badIndex=0) const
{
if (errorType == outOfMemory)
cerr << "Memory exhausted!" << endl; else
cerr << "Index " << badIndex << " out of range" << endl;
exit(1); }
// конструктор, выделение памяти и копирование в C++String MyString::MyString(char *s=””)
{
//длина включает NULL символ size = strlen(s) + 1;
str = new char [size];
//программа завершается, если память исчерпана . if (str == NULL)
Error(outOfMemory); strcpy(str,s); }
//конструктор копирования
58
MyString::MyString(const String& s) { // текущий объект длиной s
size = s.size;
str = new char [size]; if (str == NULL)
Error(outOfMemory); strcpy(str,s.str); }
//деструктор MyString::~MyString( ) {
delete [] str; }
Перегруженные операторы присваивания в классе MyString
Оператор присваивания позволяет присваивать либо объект MyString, либо строку С++ объекту MyString. Например, создаются два объекта MyString:
MyString S("Hello World!"), T;
// присваивает объект MyString объекту MyString T=S;
//присваивает строку С++ объекту MyString T="I am a C++ String";
Для того, чтобы присвоить новый MyString-объект s текущему объекту, сравнивается длина двух строк. Если они различные , оператор удаляет динамическую память текущего объекта и снова (оператором new) выделяет s.size символов. Затем s.str копируется в новую память.
// operator. MyString to MyString
MyString& MyString::operator= (const MyString& s)
{
if (s.size != size)
{
delete [] str;
str = new char [s.size]; if(str == NULL)
Error(outOfMemory); size = s.size; }
// копировать s.str и возвратить ссылку на текущий объект strcpy(str,s.str);
return *this; }
// оператор присваивания C++String to MyString MyString& MyString::operator= (char *s)
{
int slen = strlen(s) + 1; if (slen != size)
{
delete [] str;
59
str = new char [slen]; if (str == NULL)
Error(outOfMemory); size = slen;
}
strcpy(str,s);
return *this; } |
|
Перегруженные операторы сравнения |
|
В них используется C++ string функция |
strcmp. Можно использовать |
возвращаемое значение типа bool. |
|
//MyString == MyString |
|
int String::operator== (const String& s) const { |
|
return strcmp(str,s.str) == 0; } |
|
// MyString == C++String |
|
int MyString::operator== (char *s) const |
{ |
return strcmp(str,s) == 0; } |
|
//C++String == MyString. дружественная функция
//так как C++String находится в левой части
int operator== (char *str, const MyString& s) |
{ |
||
return strcmp(str,s.str) == 0; |
} |
|
|
// MyString != MyString |
|
|
|
int String::operator!= (const String& s) const |
{ |
||
return strcmp(str,s.str) != 0; |
} |
|
|
// MyString != C++String |
|
|
|
int String::operator!= (char *s) const |
{ |
|
|
return strcmp(str,s) != 0; |
} |
|
|
// C++String != MyString |
|
|
|
int operator!= (char *str, const MyString& s) |
{ |
||
return strcmp(str,s.str) != 0; |
} |
|
|
// MyString < MyString |
|
|
|
int MyString::operator< (const MyString& s) const {
return strcmp(str,s.str) < 0; |
} |
|
// MyString < C++String |
|
|
int MyString::operator< (char *s) const { |
|
|
return strcmp(str,s) < 0; |
} |
|
// C++String < MyString |
|
|
int operator< (char *str, const MyString& s) |
{ |
|
return strcmp(str,s.str) < 0; |
} |
|
Все остальные перегруженные операторы сравнения определяются аналогично.
Конкатенация : -перегруженный operator+
MyString MyString::operator+ (const MyString& s) const {
60
// создание новой строки temp с длиной len MyString temp;
int len;
// удаление NULL string , созданной при объявлении temp delete [] temp.str;
// вычисление длины результирующей строки и выделение памяти в temp len = size + s.size - 1; // только один NULL символ
temp.str = new char [len]; if (temp.str == NULL) Error(outOfMemory);
// установка размера результирующей строки и создание строки
temp.size = len; |
|
|
|
|
strcpy(temp.str,str); |
// копирование |
str в temp |
||
strcat(temp.str, s.str); |
// конкатенация |
s.str |
||
return temp; |
|
// возвратить |
temp |
|
} |
|
|
|
|
MyString MyString::operator+ (char *s) const |
||||
{ |
|
|
|
|
MyString temp; |
|
|
|
|
int len; |
|
|
|
|
delete [] temp.str; |
|
|
|
|
len = size + strlen(s); |
|
|
|
|
temp.str = new char [len]; |
|
|
||
if (temp.str == NULL) |
|
|
||
Error(outOfMemory); |
|
|
||
temp.size = len; |
|
|
|
|
strcpy(temp.str,str); |
|
|
|
|
strcat(temp.str, s); |
|
|
|
|
return temp; |
} |
|
|
|
MyString operator+ (char *cs, const |
MyString& s) |
|||
{ |
|
|
|
|
MyString temp; int len;
delete [] temp.str;
len = strlen(cs) + s.size; temp.str = new char [len]; if (temp.str == NULL)
s.Error(outOfMemory); temp.size = len; strcpy(temp.str,cs); strcat(temp.str, s.str); return temp;
}
61
void MyString::operator+= (const MyString& s)
{
char *tempstr; int len;
// вычисляется длина строки конкатенации и выделяется память в tempstr len = size + s.size - 1;
tempstr = new char [len]; if (tempstr == NULL)
Error(outOfMemory);
//копировать строку в tempstr и объединить с s.str strcpy(tempstr,str);
strcat(tempstr, s.str);
//удалить текущую строку
delete [] str;
// новая строка имеет адрес tempstr с длиной len
str = tempstr; |
|
|
size = len; |
|
|
} |
|
|
void MyString::operator+= (char *s) { |
|
|
int len; |
|
|
char *tempstr; |
|
|
len = size + strlen(s); |
|
|
tempstr = new char [len]; |
|
|
if (tempstr == NULL) |
|
|
Error(outOfMemory); |
|
|
strcpy(tempstr,str); |
|
|
strcat(tempstr, s); |
|
|
delete [] str; |
|
|
str = tempstr; |
|
|
size = len; |
} |
|
int MyString::Find(char c, int start) const |
{ |
|
int ret; |
|
|
char *p; |
|
|
p = strchr(&str[start],c); |
|
|
if (p != NULL) |
|
|
ret = int(p-str); |
|
|
else |
|
|
ret = -1; |
|
|
return ret; |
} |
|
// возвращает индекс последнего вхождения с в строке int MyString::FindLast(char c) const {
int ret; char *p;
62