Лабораторная работа №3: Перегрузка функций. Перегрузка операторов

Цель работы: 1) изучить возможности программирования классов на языке С++; 2) получить основные навыки программирования. Теоретические сведения

Дружественные классы

Разрешить элементам другого класса полный доступ к элементам данного класса, объявленным как private или protected, можно включив в определение данного класса описание friend. Пример 1.

сlass myclass {
    friend class another_class;
};

Дружественные функции

Разрешить обычной функции или функции-элементу другого класса полный доступ к элементам класса, объявленным private или protected, можно с помощью описания friend в определении данного класса. Пример 2.

сlass myclass {
    friend void another_class::member(int);
    friend void func_name(float);
};

Для друзей существуют следующие правила: на описания friend не влияют спецификаторы public, protected или private; описания friend не взаимны: если А объявляет В другом, то это не означает, что А является другом для В; дружественность не наследуется: если А объявляет В другом, классы, производные от В, не будут автоматически получать доступ к элементам А; дружественность не является переходным свойством: если А объявляет В другом, классы, производные от А, не будут автоматически признавать дружественность В.

Перегрузка функций-элементов

Функции-элементы класса могут быть перегружены. Две или несколько функций-элементов могут иметь одно и тоже имя, при условии, что списки их аргументов не совпадают. Пример 3.

class Time {
    char timestr[30];
public:
    Time();    // перегрузка конструкторов
    Time(char *str);
};

Перегрузка операций

Язык С++ позволяет определять и применять к классам обозначения операций. Эта особенность, называемая перегрузкой операций дает классам возможность вести себя подобно встроенному типу данных. Операции, допускающие перегрузку:

+    -     *    /    %    ^    &    |    ~    !    =    <    >    +=    -=    *=    /=    %=    ^=    &=
|=     <<     >>     <<=    >>=    ==    !=    <=    >=    &&    ||    ++    --    ->*    ->    ()    []

Функции-операции и перегрузка операций подчиняются следующим правилам:

Примеры программирования

Пример 4. Описать и определить класс-список, операцию –, как сортировка списка по убыванию и операцию [] получения значения по заданному номеру. Файл list.h содержит описание класса.

struct list {
    int inf;    // информационное поле
    list *next;    // указатель на следующий элемент списка
};
class spisok {
    list* l;    // указатель на начало списка
public:
    spisok (int);
    spisok (spisok&);
    void print ();
    int operator [](int);
    friend void operator(spisok&);
    ~spisok();
};

Файл list.cpp содержит определение функций-элементов.

#include <cstdlib>
#include <cstdio>
#include "list.h"
//конструктор инициализирует список из n элементов по принципу
// "очередь"
spisok::spisok (int n) {
    l = NULL;
    list *p,*pn;
    for (int i = 0; i<n; i++) {
    p = new list;
    p->inf = random(100)-50;
    p->next = NULL;
    if (l == NULL) l = p;
    else  pn->next = p;
    pn = p;
    }
}
spisok::spisok (spisok& s)
//конструктор копии класса spisok 
{    l = NULL;
    list *sp = s.l, *p, *pn;
    while (sp)
    {    p = new list;
    p->inf = sp->inf;
    p->next = NULL;
    if (l == NULL) l = p;
    else pn->next = p;
    pn = p;
    sp = sp->next;
    }
}
spisok::~spisok()
//деструктор - уничтожает объект класса список из памяти 
{    list *p;
    while (l)
    {    p = l;
    l = l->next;
    delete p;
    }
}
void spisok::print()
//функция-элемент печати содержимого списка 
{    list *p = l;
    while (p)
    {    printf ("%3d ",p->inf);
    p = p->next;
    }
    puts("");
}
int spisok::operator [] (int n)
//перегруженная операция получения значения по заданному номеру n 
{    list *p = l;
    for (int i = 1; (i<n)&& (p!=NULL); i++, p = p->next);
    if (p)  return p->inf;
    return -1000;
}
void operator(spisok& s)
//дружественный перегруженный оператор сортировки элементов
// списка по убыванию 
{    list *p = s.l;
    while (p)
    {    list *max = p, *pn = p->next;
    while (pn)
    {    if (pn->inf > max->inf) max = pn;
    pn = pn->next;
    }
    int i = max->inf;
    max->inf = p->inf;
    p->inf = i;
    p = p->next;
    }
}

Файл main.cpp содержит основную функцию.

#include <stdio.h>
#include "list.h"
int main (void) {
    spisok s1(10),        // создание списка из 10 элементов
    s2(s1),            // s2- копия списка s1
    s3(15);            // создание списка из 15 элементов
    s1.print();        // печать s1
    s2.print();        // печать s2
    s3.print();        // печать s3
    printf("Значение третьего элемента в s1=%d \n",s1[3]);
    –s3;        // сортировка s3
    s3.print();        // и печать его
    return 0;
}

В проект включены файлы: main.cpp и list.cpp. Результаты выполнения программы: -49 -50 -17 -47 -15 -29 3 -31 20 44 -49 -50 -17 -47 -15 -29 3 -31 20 44 -23 -6 -40 19 6 -46 -34 31 18 26 32 45 -29 -8 45 Значение третьего элемента в s1=-17 45 45 32 31 26 19 18 6 -6 -8 -23 -29 -34 -40 -46 Пример 5. Описать и определить класс файл и операции: = копирование файлов; -- определение самой короткой строки в файле. Файл описания класса file.h

class file {
    char *name;    // имя файла
    FILE *f;    // указатель на поток
    public:
    file (char *, char *);    // конструктор
    file &operator = (file &);    // операция копирования файлов
    friend char* operator --(file &);    // операция поиска наименьшей строки
    ~file() {   // деструктор
         fclose(f);delete name;
    };
};

Файл определения функций-элементов file.cpp

#include <stdio.h>
#include <string.h>
#include "file.h"
file::file(char *n, char *attr)    //конструктор - открывает файл
{    name = new char[strlen(n)+1];
    strcpy (name, n);
    f=fopen (name, attr);
}
file& file::operator = (file &f1)    //операция копирования файла
{    char stroka[120];
    fseek (f1.f,0,0);
    while (fgets (stroka, 120, f1.f))
    fputs (stroka, f);
    return *this;
}
char* operator -- (file &f1)    //дружественная операция поиска
// наименьшей строки в файле
{    fseek(f1.f, 0, 0);
    char *sent = new char[120];
    int minlen = 120;
    char stroka[120];
    while (fgets(stroka, 120, f1.f))
    if (strlen(stroka) < minlen) {
     minlen = strlen(stroka);
    strcpy (sent, stroka);
    }
    return sent;
}

Файл main_f.cpp с основной функцией.

#include <cstdio>
#include "file.h"
int main(void) {
    file f1("test1.txt", "rt"),    // открытие файла для чтения
    f2("test2.txt", "wt");    // открытие файла для записи
    f2 = f1;    //копирование файлов
    printf("Самая короткая строка = %s\n",f1--);
    return 0;
}

Проект содержит файлы main_f.cpp и file.cpp.

Контрольные вопросы

  1. Какие классы и функции называются дружественными?
  2. Как осуществляется перегрузка операций?
  3. Сколько аргументов требуется для определения перегруженной унарной (бинарной) операции?
  4. Чем отличается перегруженная операция ++ при ее использовании в префиксной форме от использовании в постфиксной форме?

Варианты заданий

Номер варианта Задание
1 Определить класс-строку. В класс включить два конструктора: для определения класса строки строкой символов и путем копирования другой строки (объекта класса строки). Определить операции над строками: >> перевертывание строки (запись символов в обратном порядке); ++ нахождение наименьшего слова в строке.
2 Определить класс-строку. В класс включить два конструктора: для определения класса строки строкой символов и путем копирования другой строки (объекта класса строки). Определить операции над строками: ++ преобразование символов строки в прописные (заглавные) символы; -- нахождение самого короткого слова в строке.
3 Определить класс-строку. В класс включить два конструктора: для определения класса строки строкой символов и путем копирования другой строки (объекта класса строки). Определить операции над строками: + конкатенация двух строк; ++ преобразование символов строки в строчные (маленькие) символы.
4 Определить класс-строку. В класс включить два конструктора: для определения класса строки строкой символов и путем копирования другой строки (объекта класса строки). Определить операции над строками: - удаление одной строки из другой (если одна строка является подстрокой другой); -- преобразование символов строки в строчные (маленькие) символы.
5 Определить класс список элементов. В определение класса включить два конструктора: для определения списка по его размеру и путем копирования другого списка. Определить операции над списком: | формирование нового списка из двух списков так, что каждый элемент информационного поля нового списка удовлетворяет условию: с=(а > b) ? a : b Определить функцию-элемент класса для вставки нового элемента в список на определенное место.
6 Определить класс список элементов. В определение класса включить два конструктора для определения списка по его размеру и путем копирования другого списка. Определить операции над списком: & формирование нового списка из двух списков так, что каждый элемент информационного поля нового списка удовлетворяет условию: с=(а < b) ? a : b Определить функцию-элемент класса для удаления элемента с определенного места списка.
7 Определить класс список элементов. В определение класса включить два конструктора для определения списка по его размеру и путем копирования другого списка. Определить операции над списком: ++ сортировка списка по возрастанию; -- расположение элементов списка в обратном порядке.
8 Определить класс список элементов. В определение класса включить два конструктора для определения списка по его размеру и путем копирования другого списка. Определить операции над списком: [] получение значения информационного поля указанного элемента списка; – удаление из первого списка элементов второго, если второй список входит в первый.
9 Определить класс список элементов. В определение класса включить два конструктора для определения списка по его размеру и путем копирования другого списка. Определить операции над списком: + конкатенация двух списков; & формирование нового списка из двух списка так, что каждый элемент информационного поля нового списка удовлетворяет условию: с=(а > b) ? a : b
10 Определить класс матрицу. В класс включить два конструктора для определения матрицы по количеству элементов и путем копирования другой матрицы. При задании матрицы предусмотреть ее заполнение случайными числами. Определить операции над матрицей: ++ нахождение наибольшего значения матрицы; + получение новой матрицы, каждый элемент которой равен сумме соответствующих элементов двух других матриц.
11 Определить класс матрицу. В класс включить два конструктора для определения матрицы по количеству элементов и путем копирования другой матрицы. При задании матрицы предусмотреть ее заполнение случайными числами. Определить операции над матрицей: -- нахождение наименьшего значения матрицы; - получение новой матрицы, каждый элемент которой равен разности элементов двух других матриц.
12 Определить класс стек. В класс включить два конструктора для определения стека по его размеру и путем копирования другого стека. Определить операции над стеком: + поместить элемент в стек; -- удалить элемент из стека. Определить две функции-элемента класса для выдачи на экран текущего элемента стека и содержимого стека.
13 Определить класс вектор. В класс включить два конструктора для определения вектора по его размеру и путем копирования другого вектора. При задании вектора по его размеру предусмотреть его заполнение случайными числами. Определить операции над векторами: & формирование нового вектора так, что каждый элемент нового вектора определяется следующим образом: c[i]=(a[i]>b[i])?a[i]:b[i]; ++ определить наибольший элемент вектора.
14 Определить класс вектор. В класс включить два конструктора для определения вектора по его размеру и путем копирования другого вектора. При задании вектора по его размеру предусмотреть его заполнение случайными числами. Определить операции над векторами: | формирование нового вектора так, что каждый элемент нового вектора определяется следующим образом: c[i]=(a[i]>b[i])?b[i]:a[i]; -- определить наименьший элемент вектора.
15 Определить класс вектор. В класс включить два конструктора для определения вектора по его размеру и путем копирования другого вектора. При задании вектора по его размеру предусмотреть его заполнение случайными числами. Определить операции над векторами: [] нахождение значения элемента вектора по заданному номеру; ++ сортировка элементов вектора по возрастанию.