Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
отчет кдз программирование.docx
Скачиваний:
0
Добавлен:
11.02.2026
Размер:
158.59 Кб
Скачать

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

Тест Задание №1

Предположим, что Square — это класс, а square — это объект этого класса. Какой способ перегрузки лучше использовать для следующих операторов?

Ответ:

  • бинарный оператор + лучше всего реализовать как обычную/дружественную функцию;

  • унарный оператор - лучше всего реализовать как функцию-член;

  • оператор << должен быть реализован как обычная/дружественная функция;

  • оператор = должен быть реализован как функция-член.

Задание №2

Напишите класс Average, который будет вычислять среднее значение всех передаваемых ему целых чисел. Используйте два члена: первый должен быть типа int32_t и использоваться для вычисления суммы всех передаваемых чисел, второй должен быть типа int8_t и использоваться для вычисления количества передаваемых чисел. Чтобы найти среднее значение, нужно разделить сумму на количество.

a) Текст программы:

#include <iostream>

using namespace std;

class Average

{

private:

int_least32_t m_total; // прописываем типы необходимых данных

int_least8_t m_numbers;

public:

Average()

{

}

friend std::ostream& operator<<(std::ostream& out, const Average& average) //объявляем дружественный поток для того, чтоб прописать оператор вывода

//для того, чтоб вывести члены класса

{

out << static_cast<double>(average.m_total) / average.m_numbers; //так как здесь нужно деление с плавающей запятой, а не целочисленное

//мы используем системное преобразование типов для более точного результата

return out;

}

Average& operator+=(int num) // Поскольку operator+= изменяет свой левый операнд, мы запишем его как член

{

m_total += num;

++m_numbers;

return *this;

}

};

int main() {

Average avg;

avg += 5;

std::cout << avg << '\n'; // 5 / 1 = 5

avg += 9;

std::cout << avg << '\n'; // (5 + 9) / 2 = 7

avg += 19;

std::cout << avg << '\n'; // (5 + 9 + 19) / 3 = 11

avg += -9;

std::cout << avg << '\n'; // (5 + 9 + 19 - 9) / 4 = 6

(avg += 7) += 11; // выполнение цепочки операций

std::cout << avg << '\n'; // (5 + 9 + 19 - 9 + 7 + 11) / 6 = 7

Average copy = avg;

std::cout << copy << '\n';

return 0;

}

Результат работы программы:

b) Требуется ли этому классу явный конструктор копирования или операция присваивания?

Ответ: здесь можно обойтись без явного конструктора копирования или операции присваивания.

Задание №3

Напишите свой собственный класс-массив целых чисел IntArray (не используйте std::array или std::vector). Пользователи должны передавать размер массива при создании объекта этого класса, а сам массив (переменная-член) должен выделяться динамически. Используйте макрос assert для проверки передаваемых значений, а также свой конструктор копирования и перегрузку операции присваивания, если это необходимо.

Текст программы:

#include <iostream>

#include <cassert> // для assert

class IntArray

{

private:

int m_length = 0;

int* m_array = nullptr;

public:

IntArray(int length) :

m_length{ length }

{

assert(length > 0 && "IntArray length should be a positive integer");

m_array = new int[m_length] {};

}

// Конструктор копирования, использующий глубокое копирование

IntArray(const IntArray& array) :

m_length{ array.m_length }

{

// размещаем новый массив

m_array = new int[m_length];

// копируем элементы из исходного массива в новый

for (int count = 0; count < array.m_length; ++count)

m_array[count] = array.m_array[count];

}

~IntArray()

{

delete[] m_array;

}

friend std::ostream& operator<<(std::ostream& out, const IntArray& array)

{

for (int count = 0; count < array.m_length; ++count)

{

out << array.m_array[count] << ' ';

}

return out;

}

int& operator[] (const int index)

{

assert(index >= 0);

assert(index < m_length);

return m_array[index];

}

// Оператор присваивания, использующий глубокое копирование

IntArray& operator= (const IntArray& array)

{

// защита от самоприсваивания

if (this == &array)

return *this;

// если этот массив уже существует, удалить его,

// чтобы не было утечек памяти

delete[] m_array;

m_length = array.m_length;

// размещаем новый массив

m_array = new int[m_length];

// копируем элементы из исходного массива в новый

for (int count = 0; count < array.m_length; ++count)

m_array[count] = array.m_array[count];

return *this;

}

};

IntArray fillArray() {

IntArray a(6);

a[0] = 6;

a[1] = 7;

a[2] = 3;

a[3] = 4;

a[4] = 5;

a[5] = 8;

return a;

}

int main()

{

IntArray a = fillArray();

std::cout << a << '\n';

IntArray b(1);

a = a;

b = a;

std::cout << b << '\n';

return 0;

}

Результат работы программы:

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

Во-первых, мы даже должны проверить, есть ли в источнике строка. Если это так, то мы выделяем достаточно памяти для хранения копии этой строки. И в конце нам нужно вручную скопировать строку.

Обратите внимание, что наш оператор присваивания очень похож на наш конструктор копирования, но есть три основных отличия:

-мы добавили проверку на самоприсваивание;

-мы возвращаем *this, чтобы можно было добавить оператор присваивания в цепочку;

-нам нужно явно освободить любое значение, которое уже содержится в строке (чтобы не было утечки памяти, когда данные заново размещаются позже).

Наследование в языке C++.

Тест

Задание №1

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

a) #include <iostream>

class Parent {

public:

Parent () {

std::cout << "Parent()\n";

}

~Parent () {

std::cout << "~Parent()\n";

}

};

class Child: public Parent{

public:

Child () {

std::cout << "Child()\n";

}

~Child () {

std::cout << "~Child()\n";

}

};

int main () {

Child ch;

}

Ответ:

Parent()

Child()

~Child()

~Parent()

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

b)

#include <iostream>

class Parent{

public:

Parent (){

std::cout << "Parent()\n";

}

~Parent () {

std::cout << "~Parent()\n";

}

};

class Child: public Parent {

public:

Child () {

std::cout << "Child()\n";

}

~Child () {

std::cout << "~Child()\n";

}

};

int main () {

Child ch;

Parent p;

}

Ответ:

Parent()

Child()

Parent()

~Parent()

~Child()

~Parent()

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

c) #include <iostream>

class Parent {

private:

int m_x;

public:

Parent (int x): m_x(x) {

std::cout << "Parent()\n";

}

~Parent () {

std::cout << "~Parent()\n";

}

void print () {std::cout << "Parent: " << m_x << '\n'; }

};

class Child: public Parent{

public:

Child (int y): Parent (y) {

std::cout << "Child()\n";

}

~Child () {

std::cout << "~Child()\n";

}

void print () { std::cout << "Child: " << m_x << '\n'; }

};

int main () {

Child ch (7);

ch.print ();

}

Ответ:

Программа не сработает, так как переменная m_x находится в приватной области, и класс ребенок не имеет доступа к этой закрытой области родительского класса.

d)

#include <iostream>

class Parent {

protected:

int m_x;

public:

Parent (int x): m_x(x) {

std::cout << "Parent()\n";

}

~Parent () {

std::cout << "~Parent()\n";

}

void print() { std::cout << "Parent: " << m_x << '\n'; }

};

class Child: public Parent

{

public:

Child (int y): Parent(y) {

std::cout << "Child()\n";

}

~Child () {

std::cout << "~Child()\n";

}

void print () { std::cout << "Child: " << m_x << '\n'; }

};

int main () {

Child ch(7);

ch.print();

}

Ответ:

Parent()

Child()

Child: 7

~Child()

~Parent()

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

e)

#include <iostream>

class Parent {

protected:

int m_x;

public:

Parent (int x): m_x(x) {

std::cout << "Parent()\n";

}

~Parent () {

std::cout << "~Parent()\n";

}

void print () {std::cout << "Parent: " << m_x << '\n'; }

};

class Child: public Parent {

public:

Child (int y): Parent(y)

{

std::cout << "Child()\n";

}

~Child () {

std::cout << "~Child()\n";

}

void print () { std::cout << "Child: " << m_x << '\n'; }

};

class D2: public Child {

public:

D2(int z): Child(z) {

std::cout << "D2()\n";

}

~D2() {

std::cout << "~D2()\n";

}

// Обратите внимание, здесь нет метода print()

};

int main (){

D2 d (7);

d.print ();

}

Ответ:

Parent()

Child()

D2()

Child: 7

~D2()

~Child()

~Parent()

Сначала мы создаем объект в классе D2. Для этого нужно сначала нужно создать объекты в классах Child и Parent. Так как в классе D2 нет своего метода вывода, используется метод родительского класса Child. После чего в обратном порядке созданные объекты будут уничтожены.

Задание №2

  1. Создайте классы Apple и Banana, которые наследуют класс Fruit. У класса Fruit есть две переменные-члены: name и color.

  2. Добавьте новый класс GrannySmith, который наследует класс Apple.

Текст программы:

#include <iostream>

#include <string>

using namespace std;

class Fruit {

public:

string name;

string color;

Fruit() {};

Fruit(string c) {

name = "None";

color = c;

}

};

class Apple :public Fruit {

public:

Apple(string nn) : Fruit(nn) { name = "apple"; }

string getName() { return name; }

string getColor() { return color; }

};

class Banana : public Fruit {

public:

Banana() { name = "banana"; color = "yellow"; }

string getName() { return name; }

string getColor() { return color; }

};

int main() {

Apple a("red");

Banana b;

std::cout << "My " << a.getName() << " is " << a.getColor() << ".\n";

std::cout << "My " << b.getName() << " is " << b.getColor() << ".\n";

Результат работы программы:

#include <iostream>

#include <string>

using namespace std;

class Fruit {

public:

string name;

string color;

Fruit() {};

Fruit(string c) {

name = "None";

color = c;

}

};

class Apple :public Fruit {

public:

Apple() : Fruit() { name = "Granny Smith apple"; color = "green"; }

Apple(string nn) : Fruit(nn) { name = "apple"; }

string getName() { return name; }

string getColor() { return color; }

};

class Banana : public Fruit {

public:

Banana() { name = "banana"; color = "yellow"; }

string getName() { return name; }

string getColor() { return color; }

};

class GrannySmith : Apple {

public:

GrannySmith() : Apple() {}

string getName() { return name; }

string getColor() { return color; }

};

int main() {

Apple a("red");

Banana b;

GrannySmith c;

std::cout << "My " << a.getName() << " is " << a.getColor() << ".\n";

std::cout << "My " << b.getName() << " is " << b.getColor() << ".\n";

std::cout << "My " << c.getName() << " is " << c.getColor() << ".\n";

Результат работы программы: