Skip to content
This repository was archived by the owner on Feb 11, 2023. It is now read-only.

va9abond/cs_study

Repository files navigation

Практические работы по Обектно-ориентированному программированию

https://online-edu.mirea.ru/course/view.php?id=6362


Практическая работа №1 Перегрузка операций

  • class: circle

Практическая работа №2 Массив

  • class: ArrayParent
  • subclass: ArrayChild

Вопросы:

  1. quantity и capacity, должны быть по разному устроены: quantity- количество элементов в массиве capacity- память для этих элементов в байтах, тогда capacity = sizeof(type) * quantity

    Ответ: это, конечно, хорошо, но мы вводим capacity в количестве элементов, для своего удобства, точнее для нормальной работы функции new (подробнее см. Вопрос 3 и Вопрос 15)

  2. не понятно как работает double* ptr;

    Ответ: это даст нам указатель на 1 конкретную ячейку. Конечно, сама по себе эта ячейка не массив, но на самом деле после того как выполнится команда ptr = new double[Dimension] ячейка double* ptr; окажется началом нашего массива. Но это не нулевой элемент массива. Вообщем это указатель на начало массива, потом именно по этому указателю будет индексация массива, т.е. array[index] = array[0] + index * sizeof(type) ).

  3. не понятно как работает ptr = new double[Dimension]

    Ответ: функция new последовательно выделяет память типа double(мы сами задаем какого типа будут элементы) для количества элементов равному capacity, именно поэтому capacity у нас отвечает на вопрос: под сколько элементов выделять память, т.е. capacity лучше измерять в количесве элементов.

  4. деконструктор, если бы мы не проверяли условие (ptr != NULL), то что тогда удаляла бы функция delete ?

    Ответ: вообще нужно ещё прочитать про ptrnull. А так если ptr == ptrnull, то ничего удалять не нужно, т.к. никакой памяти и так не выделено.

  5. сравнение double

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

    Популярная практика сравнения такая:

    #include <cmath>
    #include <limits>
    
    bool is_equal(double x, double y) 
    {
        return std::fabs(x - y) < std::numeric_limits<double>::epsilon();
    }
  6. может ли быть у конкретного класса абстрактный подкласс? Абстрактный класс единственный во всем дереве классов? Он обязательно должен быть корнем дерева классов?

    Ответ: да, в теории может, на практике сложновато выдумать такой пример. Абстрактных классов может быть несколько.

  7. зачем в родительском классе объявлять методы без тела? Например: int tree(vector<int>& graph);

    Ответ: если, например, родительский класс абстрактный, то нужно будет эти методы переопределить для каждого производного класса, а потом через указатель на базовый класс вызывать этот метод для каждого из подклассов. Например: родительский абстрактный класс - Mammal имеет метод Speech(), но для каждого из подклассов Cow , Dog , People , Lion этот метод должен быть определен по разному. По всей видимости это еще один способ сделать метод виртуальным.

  8. должен ли конструктор копий ArrayMaster(const ArrayMaster& array_existing) возвращать что-то? А преобразователь типов?

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

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

    • передача объекта класса в функцию
    • возврат объекта класса из функции
    • operator=
    • при создании компилятором временных объектов
  9. что хранит указатель ptr_ указатели или ссылки?

    Ответ: я думаю, что ссылки, потому что если бы он хранил указатели, нам пришлось бы их где-то разыменовывать, но мы этого нигде не делаем.

  10. double get_element(int index) или double& get_element(int index)

    Ответ: Вариант 1, иначе будет нарушена инкапсуляция.

  11. у меня есть два конструктора ArrayMaster(int Dimension = 100) и ArrayMaster(int quantity = 100, double value = 0) если я вызову один из них, не указав аргументы, который из них сработает?

    Ответ: наличие двух конструкторов по умолчанию не допускается.

  12. зачем возвращать *this в методе ArrayMaster& operator=(const vector<double>& vector)?

    Ответ: для того, что сработал цепной метод operator= , т.е. A = B = C = D. Если не возвращать *this, то на первом шаге C = D ничего не вернется и следующий шаг будет выглядеть так: B = ... Непонятно, что должно произойти с B

    Кстати выполнение такого цепного метода начнется справа-налево. Как итог будет выполнено следующее: A = D

    По шагам A = B = C = D :

    1. C = D
    2. B = C
    3. A = B (<=> A = D)
    
  13. что должен возвращать оператор = ссылку или самого себя(т.е. свою копию?)

    Ответ: ссылку на себя; см. Вопрос 12.

  14. как работает деструктор производного массива? Он вызывает деструктор родительского? Если это так, тогда зачем его вообще объявлять в производном классе?

    Ответ: конструктор при создании производного класса спускается сверху-вниз от родительского к производному, деструктор наоборот поднимается снизу вверх от нашего производного к родительскому. Почему именно так?

    Построим аналогию. При создании объекта производного типа создается объект родильского класса, этот объект родительского класса удобно считать за ядро объекта производного типа. Сначало создается ядро, после - оболочка. Сначала разрушается оболочка, потом - ядро.

  15. понятно как capacity_ связано с выделением памяти в конструкторе. Но потом при инкрементировании capacity_ (задесь ошибка! мы не инкрементируем const capacity_) количество выделенной памяти никак не изменяется, что делать? Как тогда происходит выделение новой памяти?

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


Практическая работа №3 Множественное наследование


Практическая работа №4 Строки


Практическая работа №5 Матрица

Вопросы:

  1. Лучше выделять линейную память память, это будет быстрее и легче, но будеть проблемой расширить один из массивов(массив массивов, не о матрицах). Если раньше это можно было сделать, перевыделив память под нужный массив, то сейчас нужно как-то извращаться.

  2. Как все таки сделать нормальный доступ к элементам через matrix[][]? Там два способа: 1- перегрузка оператора [], теперь он будет возвращать ссылку на строку, которая является массивом, поэтому оператор [] для нее уже определен, там будет номр, но это почему-то плохо(что-то типа двойной досткп к памяти); 2- создание нового подчего-то внутри родительского класса, через который и будет все осуществляться.

  3. Когда нужно возвращать ссылку на объект, а когда сам объект?

  4. Для чего нужнен виртуальный деструктор?

  5. Зачем нужна функция what()?

    Ответ: функция what() это метод класса std::exception, аналог метода print().

  6. Зачем нужен виртуальный деструктор?

  7. Как сделать красивый вывод матрицы в консоль?

  8. Почему если унарный минус возвращает не сам объект, а ссылку на него, то всё ломается.

  9. Почему функции ввода\вывода должны быть дружественными?

  10. Дружественные функции. Например, как работает сложение через дружественные функции, где первое слагаемое - базоваый класс, второе - мой собсвенный класс?


Практическая работа №7 Функция с переменным числом параметров

Вопросы:

  1. Почему индексация с 1?

    Ответ: чтобы корректно работал метод insert(size_ + 1, data). Чтобы добавить в конец нужно вставить элемент на одну позицию больше, чем последний. Если индекс последнего элемента size_ - 1, то необходимо вставить его на место size_, но всего элементов size_, тогда значит ли это, что последний элемент нужно заменить данным?

  2. Почему класс Object остался внутри класса LinkedList<Type>.

    Ответ: для того, чтобы нельзя было вне класса LinkedList<Type> или его наследников создать экземпляр класса Object, это дополнительная защита(см. вопрос 3)

  3. Почему методы типа remove() возвращают Type, а не экземпляр класса Object?

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


Практическая работа №7 Функция с переменным числом параметров

Вопросы:

  1. Type value_max = *++(++ptr);

    где typeid(*ptr).name() = int. Почему где-то так получается инициализировать указатель типа int к типу Type, а где-то нет.

  2. Как сделать так, чтобы внешний класс, стал дружественным для моего класса?

  3. Если объявить класс IndexOutOfBounds как чисто виртуальный, то как ловить исключения, которые наследуются от него?

  4. Как происходит поимка исключений? Как происходит ловля исключений-потомков?


About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published