diff --git a/01_week/CMakeLists.txt b/01_week/CMakeLists.txt index 5ecaacbc..166189d5 100644 --- a/01_week/CMakeLists.txt +++ b/01_week/CMakeLists.txt @@ -7,7 +7,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) # Гарантирует использов set(EXAMPLES_DIR examples) # Определим переменную с именем директории set(TASKS_DIR tasks) -add_subdirectory(tasks) +#add_subdirectory(tasks) # Создать исполняемый файл для каждого примера if (BUILD_EXAMPLES_01_WEEK) @@ -32,4 +32,4 @@ if (BUILD_EXAMPLES_01_WEEK) add_example(functions_recursive ${EXAMPLES_DIR}/functions_recursive.cpp) add_example(io_streams ${EXAMPLES_DIR}/io_streams.cpp) add_example(main_with_args ${EXAMPLES_DIR}/main_with_args.cpp) -endif() \ No newline at end of file +endif() diff --git a/01_week/tasks/addition/addition.cpp b/01_week/tasks/addition/addition.cpp index 92872802..1290aa2b 100644 --- a/01_week/tasks/addition/addition.cpp +++ b/01_week/tasks/addition/addition.cpp @@ -3,5 +3,7 @@ int64_t Addition(int a, int b) { - throw std::runtime_error{"Not implemented"}; -} \ No newline at end of file +int64_t res = static_cast(a); // Присвоение и приведение типов. +res = res + static_cast(b); // Вычисление суммы и приведение типов. + return res; +} diff --git a/01_week/tasks/char_changer/char_changer.cpp b/01_week/tasks/char_changer/char_changer.cpp index 3a7344d9..a4011b6c 100644 --- a/01_week/tasks/char_changer/char_changer.cpp +++ b/01_week/tasks/char_changer/char_changer.cpp @@ -1,7 +1,58 @@ #include #include +// !!!! ВАЖНО !!!! Изначальное объявление функции изменено в части инициализации передаваемого элемента с «char delimiter = ' '» на «char delimiter». +size_t CharChanger(char array[], size_t size, char delimiter) { -size_t CharChanger(char array[], size_t size, char delimiter = ' ') { - throw std::runtime_error{"Not implemented"}; + char symbol; // Актуальный символ. + char prevSymbol; // Предыдущий символ. + char newSymbol = 0; // Новый символ. + size_t i = 0; // счетчик. + size_t j = 0; // счетчик итераций для индексации элементов обновленного массива. + char numMatch = 0; + prevSymbol = ~array[i]; + + + while(i != size) // Проверка. + { + symbol = array[i]; // Присваиваем значение элемента массива для анализа. + + if(('0' <= symbol) && (symbol <= '9')) newSymbol = '*'; // Если символ - цифра меняем ее на ‘*’. + else if(('a' <= symbol) && (symbol <= 'z')) newSymbol = symbol - ('a' - 'A'); // Если символ – сточная буква, меняем ее на заглавную. + else if(('A' <= symbol) && (symbol <= 'Z')) newSymbol = symbol; // Если символ – заглавная буква – ничего не делаем. + else if(symbol == ' ') newSymbol = delimiter; // Пробел меняем на спец символ. + else newSymbol = '_'; // Все остальные символы заменяем на ‘_’. + + if(symbol != prevSymbol) // Проверка совпадения текущего символа с предыдущим. + { + prevSymbol = symbol; // Запоминаем символ. + + if(numMatch != 0) // Количество совпадений равно 0? + { + if(array[i-1] != ' ') // Проверяем, прядущий проверяемый символ – пробел. Если да то не нужно печатать количество со впавших символов. + { + if(numMatch >= 9) array[j] = '0'; // Если количество совпадении больше 10 (9 т.к. 10-1). Печатаем 0. + else array[j] = '0' + numMatch + 1; // Иначе печатаем количество одинаковых символов подряд. + j++; + } + } + numMatch = 0; // Обнуляем количество совпадений. + array[j] = newSymbol; // Записываем изменений элемент в массив символов. + j++; // Увеличение счетчика итераций для индексации элементов обновленного массива. + + } + else numMatch++; //Символы совпали, увеличиваем счетчик итераций. + + i++; // Счетчик для анализируемых элементов массива. + + if(symbol == '\0') { // Проверка символа окончания строки. + j = j-1; // уменьшаем счетчик для записи символа окончания строки в обновлённый массив. + array[j] = '\0'; // записываем символ окончания строки + return j; // возвращаемое значение - номер последнего элемента обнавленного массива. + } + } + array[j] = '\0'; + + return j; + } diff --git a/01_week/tasks/check_flags/check_flags.cpp b/01_week/tasks/check_flags/check_flags.cpp index 75e7c652..8d065e0b 100644 --- a/01_week/tasks/check_flags/check_flags.cpp +++ b/01_week/tasks/check_flags/check_flags.cpp @@ -1,7 +1,6 @@ #include #include - enum class CheckFlags : uint8_t { NONE = 0, TIME = (1 << 0), @@ -14,5 +13,68 @@ enum class CheckFlags : uint8_t { }; void PrintCheckFlags(CheckFlags flags) { - throw std::runtime_error{"Not implemented"}; + + char buf[40] = {'\0'}; + uint8_t data = static_cast(flags); // приведение типа к uint8_t т.к. CheckFlags типа uint8_t + size_t i = 0; + + buf[0] = '['; + i =1; + // Проверка наличия "лишних флагов", ALL приводим к типу uint8_t + // Если есть лишние флаги, выходим. + if(data > static_cast(CheckFlags::ALL)) return; + if(data != 0) + { + if(data & static_cast(CheckFlags::TIME)){ + if(buf[i-1] != '['){ + buf[i] = ','; + i++; + } + + buf[i++] = 'T'; buf[i++] = 'I'; buf[i++] = 'M'; buf[i++] = 'E'; + } + if(data & static_cast(CheckFlags::DATE)){ + if(buf[i-1] != '['){ + buf[i] = ','; + i++; + } + + buf[i++] = 'D'; buf[i++] = 'A'; buf[i++] = 'T'; buf[i++] = 'E'; + } + if(data & static_cast(CheckFlags::USER)){ + if(buf[i-1] != '['){ + buf[i] = ','; + i++; + } + + buf[i++] = 'U'; buf[i++] = 'S'; buf[i++] = 'E'; buf[i++] = 'R'; + } + if(data & static_cast(CheckFlags::CERT)){ + if(buf[i-1] != '['){ + buf[i] = ','; + i++; + } + + buf[i++] = 'C'; buf[i++] = 'E'; buf[i++] = 'R'; buf[i++] = 'T'; + } + if(data & static_cast(CheckFlags::KEYS)){ + if(buf[i-1] != '['){ + buf[i] = ','; + i++; + } + + buf[i++] = 'K'; buf[i++] = 'E'; buf[i++] = 'Y'; buf[i++] = 'S'; + } + if(data & static_cast(CheckFlags::DEST)){ + if(buf[i-1] != '['){ + buf[i] = ','; + i++; + } + + buf[i++] = 'D'; buf[i++] = 'E'; buf[i++] = 'S'; buf[i++] = 'T'; + } + } + + buf[i] = ']'; + std::cout << buf; } diff --git a/01_week/tasks/length_lit/length_lit.cpp b/01_week/tasks/length_lit/length_lit.cpp index e69de29b..a2956b03 100644 --- a/01_week/tasks/length_lit/length_lit.cpp +++ b/01_week/tasks/length_lit/length_lit.cpp @@ -0,0 +1,62 @@ + +// Константы для преобразования +#define IN_TO_CM 2.54 +#define FT_TO_IN 12.0 +#define M_TO_CM 100.0 + + +constexpr double operator"" _ft_to_m(long double x) { + return static_cast(x / M_TO_CM * FT_TO_IN * IN_TO_CM); // преобразование фт в метры +} + +constexpr double operator"" _ft_to_cm(long double x) { + return static_cast(x * FT_TO_IN * IN_TO_CM); // преобразование фт в см +} + +constexpr double operator"" _ft_to_in(long double x) { + return static_cast( x * FT_TO_IN); // преобразование фт в дюймы +} + + +//Дюймы +constexpr double operator"" _in_to_m(long double x) { + return static_cast(x * IN_TO_CM / M_TO_CM); +} + +constexpr double operator"" _in_to_cm(long double x) { + return static_cast(x * IN_TO_CM); // +} + +constexpr double operator"" _in_to_ft(long double x) { + return static_cast(x / FT_TO_IN); // +} + +//Метры +constexpr double operator"" _m_to_ft(long double x) { + return static_cast(x / IN_TO_CM / FT_TO_IN * M_TO_CM); // +} + +constexpr double operator"" _m_to_cm(long double x) { + return static_cast( x * M_TO_CM); // +} + +constexpr double operator"" _m_to_in(long double x) { + return static_cast(x / IN_TO_CM * M_TO_CM); // +} + + +//см +constexpr double operator"" _cm_to_ft(long double x) { + return static_cast(x / FT_TO_IN / IN_TO_CM); // +} + +constexpr double operator"" _cm_to_m(long double x) { + return static_cast(x / M_TO_CM); // +} + +constexpr double operator"" _cm_to_in(long double x) { + return static_cast(x / IN_TO_CM); // +} + + + diff --git a/01_week/tasks/print_bits/print_bits.cpp b/01_week/tasks/print_bits/print_bits.cpp index a48a43c1..242e6e9f 100644 --- a/01_week/tasks/print_bits/print_bits.cpp +++ b/01_week/tasks/print_bits/print_bits.cpp @@ -3,5 +3,67 @@ void PrintBits(long long value, size_t bytes) { - throw std::runtime_error{"Not implemented"}; + +// Защита от неверного значения размера в байтах. Размер в байтах должен быть от 1 до 8. +if(bytes == 0) return; +if(bytes > 8) return; + +char buf[90] = {'\0'}; // Объявление и инициализация массива '\0'. + +// !!!! Важно!!!!! +// при отладке были проблемы, по ну установленной причине при переводе от 5 до 8 байт некорректно выводилось значение. Связано это было с некорректным смещением 0х01. Даже если 0х01 присваивалось 64 битной переменной при смещении на значение от 32 до 64, результат был не верный, в результате анализа добегу переменная смещалось на 32 и все. +// Поэтом начались танцы с бубном и все переменные сделаны 8 битными. +unsigned long long count = 0; // Счётчик проанализированных бит +unsigned long long temp = 0; +unsigned long long sizeBit = bytes*8; // количество бит для вывода +unsigned long long countFour = 0; +unsigned long long countBuf = 0; // счетчик для элементов массива +unsigned long long countBit = 0; +char res; // +unsigned long long tempBit = 0; + + buf[0] = '0'; // присвоение значения первому элементу массива + buf[1] = 'b'; // присвоение значения второму элементу массива + + countBuf = 2; + + while( count < sizeBit) // Проверка количество проанализированных бит совпала с требуемым? + { // Нет, анализируем. + + //Усложнённая конструкция но без нее работало не корректно. + temp = (sizeBit-1 - count); + countBit = 0; + tempBit = 0x01; + while(temp > countBit) + { + countBit ++; + tempBit = tempBit << 1; + } + //Конец сложной конструкции. ее можно было свирнуть до temp = 0х01 << (sizeBit-1 - count); но так не работало при значении coun< 32. + + temp = tempBit & value; // определение 0 или 1 для конкретного бита. + + if(temp) res = '1'; + else res = '0'; + + buf[countBuf + count] = res; // Запись значения в массив. + + count++; // Увеличиваема счетчик + + if(countFour == 3) // После 4х значении надо вывести символ ''' (номер 39 в таблице ASCII). + { + if(sizeBit != count) + { + buf[countBuf + count] = 39; + countBuf++; + } + countFour = 0; + } + else countFour++; + + } + buf[countBuf+count] = '\n'; // последний элемент массива '\n' + + + std::cout << buf; // Вывод результата. } diff --git a/01_week/tasks/quadratic/quadratic.cpp b/01_week/tasks/quadratic/quadratic.cpp index abf7d632..b00486ae 100644 --- a/01_week/tasks/quadratic/quadratic.cpp +++ b/01_week/tasks/quadratic/quadratic.cpp @@ -1,6 +1,60 @@ #include - +#include +#include void SolveQuadratic(int a, int b, int c) { - throw std::runtime_error{"Not implemented"}; -} \ No newline at end of file + float x1 = 0.0; // Превый корень уравнения + float x2 = 0.0; // Второй корень уравнения. + + float fA = static_cast(a); // Явное приведение типа int к float + float fB = static_cast(b); // Явное приведение типа int к float + float fC = static_cast(c); // Явное приведение типа int к float + float disc = static_cast(c); // Явное приведение типа int к float + float negativeUnit = -1; // Защита для формул при умножении на -1 + + // Если a, b, c равны нулю – решений бесконечно много. + if((a == 0) && (b == 0) && (c == 0)){ + std::cout <<"infinite solutions"; + return; + } + + // Если a, b равны нулю – решений а не равно нулю –решений нет! + if((a == 0) && (b == 0) && (c != 0)) + { + std::cout <<"no solutions"; + return; + } + + // Если x = 0 + if(((a == 0) && (c == 0)) || ((b == 0) && (c == 0))) + { + std::cout <<"0"; + return; + } + + + if(a == 0) // Своего рода ускорение вычисления при a равном нулю + { + x1 = (-fC)/fB; + std::cout << std::setprecision(6) << x1; + } + else // Решение через дискрименант + { + disc = fB * fB - 4 * fA * fC; + + if(disc < 0) + { + std::cout <<"no solutions"; + return; + } + + x1 = (negativeUnit * fB - sqrt(disc))/(2*fA); + x2 = (negativeUnit * fB + sqrt(disc))/(2*fA); + + // определяем вывод в зависимости от X1 и X2 + if (x1 == x2) std::cout << std::setprecision(6) << x1 ; + else if(x1 < x2) std::cout << std::setprecision(6) << x1 << ' ' << x2; + else std::cout << std::setprecision(6) << x2 << ' ' << x1; + + return; + } diff --git a/01_week/tasks/rms/rms.cpp b/01_week/tasks/rms/rms.cpp index 6882f0a9..9df36853 100644 --- a/01_week/tasks/rms/rms.cpp +++ b/01_week/tasks/rms/rms.cpp @@ -1,7 +1,16 @@ -#include +#include #include double CalculateRMS(double values[], size_t size) { - throw std::runtime_error{"Not implemented"}; -} \ No newline at end of file + double sum = 0.0; + + if(0 == size) return 0.0;// + if(&values[0] == nullptr) return 0.0; + + for(size_t i = 0; i < size; i++){ + sum += values[i]*values[i]; + } + + return std::sqrt(sum / size); +} diff --git a/02_week/CMakeLists.txt b/02_week/CMakeLists.txt index 625e6fbe..856d2248 100644 --- a/02_week/CMakeLists.txt +++ b/02_week/CMakeLists.txt @@ -7,7 +7,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) # Гарантирует использов set(EXAMPLES_DIR examples) # Определим переменную с именем директории set(TASKS_DIR tasks) -add_subdirectory(tasks) +#add_subdirectory(tasks) # Создать исполняемый файл для каждого примера if (BUILD_EXAMPLES_02_WEEK) @@ -17,4 +17,4 @@ if (BUILD_EXAMPLES_02_WEEK) add_example(ptr_access ${EXAMPLES_DIR}/ptr_access.cpp) add_example(ptr_arithmetic ${EXAMPLES_DIR}/ptr_arithmetic.cpp) add_example(reinterpret ${EXAMPLES_DIR}/reinterpret.cpp) -endif() \ No newline at end of file +endif() diff --git a/03_week/CMakeLists.txt b/03_week/CMakeLists.txt index e5df5bac..fb663b00 100644 --- a/03_week/CMakeLists.txt +++ b/03_week/CMakeLists.txt @@ -7,11 +7,11 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) # Гарантирует использов set(EXAMPLES_DIR examples) # Определим переменную с именем директории set(TASKS_DIR tasks) -add_subdirectory(tasks) + #add_subdirectory(tasks) # Создать исполняемый файл для каждого примера if (BUILD_EXAMPLES_03_WEEK) add_example(struct_examples ${EXAMPLES_DIR}/struct_examples.cpp) add_example(union_examples ${EXAMPLES_DIR}/union_examples.cpp) add_example(vector_examples ${EXAMPLES_DIR}/vector_examples.cpp) -endif() \ No newline at end of file +endif() diff --git a/04_week/tasks/CMakeLists.txt b/04_week/tasks/CMakeLists.txt index 6ddf92b4..b42c26a7 100644 --- a/04_week/tasks/CMakeLists.txt +++ b/04_week/tasks/CMakeLists.txt @@ -1,4 +1,3 @@ add_subdirectory(stack) -add_subdirectory(queue) + add_subdirectory(ring_buffer) -add_subdirectory(phasor) \ No newline at end of file diff --git a/04_week/tasks/phasor/phasor.cpp b/04_week/tasks/phasor/phasor.cpp index 3ec1b9ad..fa99de69 100644 --- a/04_week/tasks/phasor/phasor.cpp +++ b/04_week/tasks/phasor/phasor.cpp @@ -6,5 +6,5 @@ struct AlgTag {}; class Phasor { - + }; diff --git a/04_week/tasks/ring_buffer/ring_buffer.cpp b/04_week/tasks/ring_buffer/ring_buffer.cpp index e2b57ba2..ae43a220 100644 --- a/04_week/tasks/ring_buffer/ring_buffer.cpp +++ b/04_week/tasks/ring_buffer/ring_buffer.cpp @@ -1,6 +1,194 @@ #include + class RingBuffer { +public: + + // ---- Конструкторы ----- + explicit RingBuffer(size_t capacity); // Конструктор от емкости + RingBuffer(size_t capacity, int initialValue); // Конструткор от емкасти и начального значения + RingBuffer(std::initializer_list init); // конструктор по размеру контейнера + + // ---- Методы ----- + void Push(int value); + bool TryPush(int value); + + bool Pop(); + bool TryPop(int& value); + + int& Front(); + const int& Front() const; + int& Back(); + const int& Back() const; + + bool Empty() const {return countElem == 0;} + bool Full() const {return countElem == buf.capacity();} + size_t Size() const{return countElem;} + size_t Capacity() const{return buf.capacity();} + + void Clear() {prevElem = nextElem = countElem = 0;} + void Resize(size_t newCapacity); + + std::vector Vector() const; + + bool operator==(const RingBuffer& other) const; + bool operator!=(const RingBuffer& other) const; + int& operator[](size_t index); + const int& operator[](size_t index) const; + +private: + std::vector buf; + size_t prevElem = 0; + size_t nextElem = 0; + size_t countElem = 0; // счетчик текущего элемента + + + size_t NextIndex(size_t i) const { + if((i + 1) >= buf.capacity()) return 0; + return i+1; + } + }; + +RingBuffer::RingBuffer(size_t capacity) { + if(capacity == 0) capacity = 1; // Емкость 0 не должна быть + buf.reserve(capacity); + buf.resize(capacity); + prevElem = 0; + nextElem = 0; + countElem = 0; +} + +RingBuffer::RingBuffer(size_t capacity, int initialValue) { + + if(capacity == 0) capacity = 1; // Емкость 0 не должна быть + buf.reserve(capacity); + buf.resize(capacity); + prevElem = 0; + nextElem = 0; + countElem = 0; + for (size_t i = 0; i < capacity; ++i) Push(initialValue); + +} + +RingBuffer::RingBuffer(std::initializer_list init) { + + size_t capacity = init.size(); + if(capacity == 0) capacity = 1; // Емкость 0 не должна быть + + buf.reserve(capacity); + buf.resize(capacity); + prevElem = 0; + nextElem = 0; + countElem = 0; + + for (int value : init) Push(value); +} + +void RingBuffer::Push(int value) { + if (Full()) { + buf[nextElem] = value; + prevElem = NextIndex(prevElem); + nextElem = NextIndex(nextElem); + } else { + buf[nextElem] = value; + nextElem = NextIndex(nextElem); + ++countElem; + } +} + +bool RingBuffer::TryPush(int value) { + if (Full()) return false; + buf[nextElem] = value; + nextElem = NextIndex(nextElem); + ++countElem; + return true; +} + +bool RingBuffer::Pop() { + if (Empty()) return false; + prevElem = NextIndex(prevElem); + --countElem; + return true; +} + +bool RingBuffer::TryPop(int& value) { + if (Empty()) return false; + value = buf[prevElem]; + Pop(); + return true; +} + +int& RingBuffer::operator[](size_t i) { + if(i >= countElem) return buf[countElem]; + return buf[((i + prevElem)%buf.capacity())]; + +} + +const int& RingBuffer::operator[](size_t i) const { + if(i >= countElem) return buf[countElem]; + return buf[((i + prevElem)%buf.capacity())]; +} + +int& RingBuffer::Front() { + if(nextElem == 0) return buf[buf.capacity() - 1]; + return buf[nextElem - 1]; +} + +const int& RingBuffer::Front() const { + if(nextElem == 0) return buf[buf.capacity() - 1]; + return buf[nextElem - 1]; +} + +int& RingBuffer::Back() { + return buf[prevElem]; +} + +const int& RingBuffer::Back() const { + return buf[prevElem]; +} + + + + + + +void RingBuffer::Resize(size_t newCapacity) { + newCapacity = newCapacity ? newCapacity : 1; + if (newCapacity == buf.capacity()) return; + + size_t newCount = countElem < newCapacity ? countElem : newCapacity; + std::vector newBuffer = {}; + newBuffer.reserve(newCapacity); + newBuffer.resize(newCapacity); + + for (size_t i = 0; i < newCount; ++i) { + newBuffer[i] = (*this)[countElem - newCount + i]; + } + + buf = std::move(newBuffer); + prevElem = 0; + nextElem = newCount % newCapacity; + countElem = newCount; +} + +std::vector RingBuffer::Vector() const { + std::vector result; + result.reserve(countElem); + for (size_t i = 0; i < countElem; ++i) result.push_back((*this)[i]); + return result; +} + +bool RingBuffer::operator==(const RingBuffer& other) const { + if (countElem != other.countElem || buf.capacity() != other.buf.capacity()) + return false; + for (size_t i = 0; i < countElem; ++i) + if ((*this)[i] != other[i]) return false; + return true; +} + +bool RingBuffer::operator!=(const RingBuffer& other) const { + return !(*this == other); +} diff --git a/04_week/tasks/stack/stack.cpp b/04_week/tasks/stack/stack.cpp index 222e4ffc..04841d83 100644 --- a/04_week/tasks/stack/stack.cpp +++ b/04_week/tasks/stack/stack.cpp @@ -2,5 +2,71 @@ class Stack { + public: + // методы: + + void Push(int newData); // Добавление нэлемента в стек + + bool Pop(void); // удаление элемента с верхушки стека + + int& Top(); // Доступ к элементу на вершине стека + + const int& Top() const; // Доступ к элементу на вершине стека + + bool Empty() const; // Проверка отсутсвия элементов на вершине + + size_t Size() const; // Размер стека + + void Clear(); // Очистка стека + + void Swap(Stack& other); // меняем местами элементы + + // определение для == + bool operator==(const Stack& x) const {return data == x.data;} + + // определение для != + bool operator!=(const Stack& x) const {return !(data == x.data);} + + private: + std::vector data; // данные }; + + + +// в Методах для Stack используем методы для vector + +void Stack::Push(int newData){ + data.push_back(newData); +} + +bool Stack::Pop(void){ + if(data.empty()) return false; + data.pop_back(); + return true; +} + +int& Stack::Top(){ + return data.back(); +} + +const int& Stack::Top() const{ + return data.back(); +} + +bool Stack::Empty() const{ + return data.empty(); +} + +size_t Stack::Size() const{ + return data.size(); +} + +void Stack::Clear() { + data.clear(); +} + +void Stack::Swap(Stack& other){ + data.swap(other.data); +} + diff --git a/05_week/tasks/cow_string/cow_string.cpp b/05_week/tasks/cow_string/cow_string.cpp index 34d59738..ea059e1a 100644 --- a/05_week/tasks/cow_string/cow_string.cpp +++ b/05_week/tasks/cow_string/cow_string.cpp @@ -1,6 +1,284 @@ +/* + Коровья строка +Существуют разные подходы к управлению данными при копировании. Можно выделить следующие семантики: + +value-семантика - при копировании объекта происходит глубокое копирование (deep copy), все значения полностью объекта полностью копируются (реализовано в большинстве классов стандартной библиотеки C++). +reference-семантика - при копировании объекта его содержимое не копируется, а разделяется между копиями посредством ссылки или указателя. Также существует подход, который называют поверхностное копирование (shallow copy), когда происходит копирование только верхнего уровня оригинального объекта, а ссылки на внутренние части объекта остаются общими (реализовано по умолчанию в некоторых других языках). +cow-семантика (copy-on-write) - при копировании объекта создается shallow copy, что позволяет использовать объект на чтение без дополнительных затрат ресурсов, но при изменении объекта создается настоящая глубокая копия и изменения вносятся уже в копию. Таким образом, копирование совершается только при внесении изменений, что в определенных сценариях использования объекта позволяет увеличить производительность и уменьшить затраты на ресурсы. +Cow-семантика применяется в реализации строк Яндекса и фреймворка Qt. + +Необходимо реализовать класс CowString, который представляет собой упрощенную реализацию строки с copy-on-write семантикой. + +Класс предоставляет следующий функционал: + +Конструктор по умолчанию — создает пустую строку +Конструктор от const char* +Конструктор от std::string +Конструктор копирования — увеличивает счетчик ссылок, не копирует данные +Оператор присваивания копированием — увеличивает счетчик ссылок, не копирует данные +Конструктор перемещения +Оператор присваивания перемещением +Деструктор +Методы НЕ вызывающие копирования: + +Метод Size - возвращает длину строки без учета терминирующего нуля \0 +Метод ToCstr - возвращает указатель на данные +Метод ToString - возвращает std::string +Оператор [] - доступ к символу для чтения +Оператор неявного преобразования к C-строке +Методы, обеспечивающие модификацию на собственной копии данных: + +Оператор [] - доступ к символу для записи +Метод Append - добавляет строку из C-строки или std::string +Метод Substr - принимает позицию и количество символов (по умолчанию от начала до конца строки), возвращает соответствующую подстроку. Если позиция начала превышает длину, то возвращаем пустую строку. +Метод Clear - очистка строки +Для реализации строки удобно вынести все необходимые поля в отдельную структуру и кроме этого хранить в ней счетчик ссылок ref_count. + +Правильно поддерживая ref_count всегда будет известно, когда нужно удалять данные строки или когда нужно превращать shallow-copy в deep-copy. + +Примечание +Запрещено использовать std::string в реализации и умные указатели +Рекомендуется определять методы вне класса +При необходимости вспомогательные методы реализуются в закрытой части класса +Тесты оператор [] не проверяют на индекс вне диапазона +Проблемы реализации +В предполагаемой реализации имеется следующая проблема: при использовании модифицирующей версии оператора [] пользователь может сохранить ссылку и модифицировать символ позже, что нарушит cow-семантику. Для упрощения решать её не требуется. В Qt используют QCharRef для решения этой проблемы. +*/ #include -#include +#include class CowString { +private: + struct StringData { + char* data; // указатель на данные + size_t size; // размер + size_t capacity; // емкость + int ref_count; // // количество копий. int а не size_t т.к. проще отслеживать переход через 0 + + StringData(const char* buf, size_t len) : size(len), capacity(len + 1), ref_count(1) { + data = new char[capacity]; // выделение места для данных + for(size_t i = 0; i < len; i++) data[i] = buf[i]; // копирование данных + data[len] = '\0'; // последний символ + } + + + StringData(size_t cap) : size(0), capacity(cap), ref_count(1) { + data = new char[capacity]; + data[0] = '\0'; // обнуление нулевого он же последний элемент + } + + + // Диструктор + ~StringData() { delete[] data;} + + + void release_ref() { + if (--ref_count == 0) { + delete this; + } + } + }; + + StringData* str_data; + + void detach() { + if (str_data->ref_count > 1) { + StringData* new_data = new StringData(str_data->data, str_data->size); + str_data->release_ref(); + str_data = new_data; + } + } + + void ensure_unique() { + if (str_data->ref_count > 1) { + StringData* new_data = new StringData(str_data->data, str_data->size); + str_data->release_ref(); + str_data = new_data; + } + } + +public: + inline static const size_t npos = -1; + + // Конструктор по умолчанию — создает пустую строку + CowString() : str_data(new StringData(1)) {} + + + // Конструктор от const char* + CowString(const char* str) { + size_t len = 1; // если nullptr то останится 1 + + if (str != nullptr) len = std::strlen(str); // не nullptr значит задаем длину + + str_data = new StringData(str, len); + } + + + // Конструктор от std::string + CowString(const std::string& str) : str_data(new StringData(str.c_str(), str.length())) {} + + // Конструктор копирования — увеличивает счетчик ссылок, не копирует данные + CowString(const CowString& other) : str_data(other.str_data) { + str_data->ref_count++; // + } + + + // Оператор присваивания копированием — увеличивает счетчик ссылок, не копирует данные + CowString& operator=(const CowString& obj) { + if (this != &obj){ // есть, что копирвать. + str_data->release_ref() ; + str_data = obj.str_data; + str_data->ref_count++; + } + return *this; + } + + + // Конструктор перемещения + CowString(CowString&& other) noexcept : str_data(other.str_data) { other.str_data = new StringData(1); } // новую структуру создали при заполнении полей, сейчас надо "освободить" перемещенную. + + + + // Оператор присваивания перемещением + CowString& operator=(CowString&& obj) noexcept { + if (this != &obj) { // перемещние обьекта, проверяем, что перемещаем не сами себя + str_data->release_ref(); + str_data = obj.str_data; + obj.str_data = new StringData(1); + } + return *this; + } + + + // Деструктор + ~CowString() { str_data->release_ref();} + + + + + //Оператор неявного преобразования к C-строке +// Оператор неявного преобразования к C-строке +// Методы, обеспечивающие модификацию на собственной копии данных: + // Метод Size - возвращает длину строки без учета терминирующего нуля \0 + size_t Size() const { return str_data->size; } + + // Метод ToCstr - возвращает указатель на данные + const char* ToCstr() const { return str_data->data; } + + // Метод ToString - возвращает std::string + std::string ToString() const { return std::string(str_data->data, str_data->size); } + + // Оператор [] - доступ к символу для чтения + const char& operator[](size_t i) const { return str_data->data[i]; } + + // Оператор неявного преобразования к C-строке + operator const char*() const { return str_data->data; } + + +// Методы, обеспечивающие модификацию на собственной копии данных: + // Оператор [] - доступ к символу для записи + char& operator[](size_t i) { + ensure_unique(); + return str_data->data[i]; + } + + // Метод Append - добавляет строку из C-строки + CowString& Append(const char* obj) { + if (obj == nullptr || *obj == '\0') return *this; // проверка на самого себя + + size_t len = std::strlen(obj); // длина + + if (len == 0) return *this; // защита + + ensure_unique(); + + size_t new_size = str_data->size + len; // расчет новой емкости буфера + // возмоно емкостьисходного больше чем требуется для результирующей строки + if (new_size + 1 > str_data->capacity) {// добавляем строку + size_t new_capacity = new_size + 1; // + StringData* new_data = new StringData(new_capacity); // выделение памяти под увеличенную строку + // for(size_t i = 0; i < str_data->size; i++) new_data->data[i] = str_data->data[i]; + // for(size_t i = 0; i < len; i++) new_data->data[new_size - len + i] = obj->data[i]; + std::memcpy(new_data->data, str_data->data, str_data->size); // перемещение первойчасти + std::memcpy(new_data->data + str_data->size, obj, len); // перемещение второй части + new_data->data[new_size] = '\0'; + new_data->size = new_size; + + str_data->release_ref(); + str_data = new_data; + } + else { + std::memcpy(str_data->data + str_data->size, obj, len); // перемещение + str_data->size = new_size; + str_data->data[new_size] = '\0'; + } + + return *this; + } + + // Метод Append для std::string + CowString& Append(const std::string& str) { return Append(str.c_str()); } + + // Метод Substr - принимает позицию и количество символов (по умолчанию от начала до конца строки), возвращает соответствующую подстроку. Если позиция начала превышает длину, то возвращаем пустую строку. + CowString Substr(size_t pos = 0, size_t count = npos) const { + if (pos >= str_data->size) return CowString(); // проверкаб что позиция не больше размера + + size_t start = pos; + size_t len = ((count == npos) || (count > str_data->size - pos)) ? str_data->size - pos : count; + + char* buf = new char[len + 1]; // выделение места для данных + std::memcpy(buf, str_data->data + start, len); // копирование данных + buf[len] = '\0'; // последний символ + + CowString result(buf); // формируем даныне + delete[] buf; // освобождение памяти + + return result; + } + + // Метод Clear + void Clear() { + if (str_data->size == 0) return; // данных нет + + if (str_data->ref_count > 1) { // не первая копия. очищаем и удаляем + str_data->release_ref(); + str_data = new StringData(1); + } + else { // первый экземпляр, просто очистка размера и данных + str_data->size = 0; + str_data->data[0] = '\0'; + } + } + + // Метод Empty + bool Empty() const { return str_data->size == 0; } // Сокращаем размер до 0 + + // Метод Find для символа + size_t Find(char symbl, size_t i = 0) const { + if (i >= str_data->size) return npos; // искомая позиция вне размера данных + + for (size_t j = i; j < str_data->size; ++j) + if (str_data->data[j] == symbl) return j; // Символ найден + + + return npos; // ничего не найдено + } + + // Метод Find для C-строки + size_t Find(const char* str, size_t pos = 0) const { + if (str == nullptr) return npos; // пустой указатель + + if (*str == '\0') return 0; // оказались в конце строки + + if (pos >= str_data->size) return npos; // начало поиска больше размера + + size_t len = std::strlen(str); + + if (len > str_data->size - pos) return npos; + for (size_t i = pos; i <= str_data->size - len; ++i) { + if (std::memcmp(str_data->data + i, str, len) == 0) return i; // послеовательность обнаружен + } + return npos; + } }; diff --git a/05_week/tasks/simple_vector/simple_vector.cpp b/05_week/tasks/simple_vector/simple_vector.cpp index 9b2ea971..6dd908c5 100644 --- a/05_week/tasks/simple_vector/simple_vector.cpp +++ b/05_week/tasks/simple_vector/simple_vector.cpp @@ -1,5 +1,271 @@ +/*ектор чисел +Необходимо реализовать класс SimpleVector, представляющий упрощенную реализацию контейнера std::vector для целочисленных элементов типа int в динамической памяти. +Класс предоставляет следующий функционал: + +Конструктор по умолчанию +Конструктор, принимающий размер вектора и заполняющий его нулями +Конструктор, принимающий список инициализации std::initializer_list, что позволит писать SimpleVector v = {1, 3, 5} +Конструктор копирования +Конструктор перемещения +Операторы присваивания копированием +Оператор присваивания перемещением +Деструктор +Метод Swap - принимает другой вектор и меняет содержимое текущего вектора с ним местами +Операторы индексирования []- позволяет изменять содержимое для неконстантного вектора +Метод Size - возвращает число элементов в векторе +Метод Capacity - возвращает текущее число выделенных ячеек памяти под вектор. +Метод Empty - возвращает true, если вектор пуст +Метод Data - прямой доступ к памяти, не позволяющий вносить изменения +Метод PushBack который вставляет элемент в конец вектора. +Метод PopBack - удаляет последний элемент вектора. При этом изменяется только размер, выделенную память изменять не нужно. +Метод Insert - принимает позицию (или const int*) и элемент, вставляет элемент перед указанной позицией, возвращает указатель на вставленный элемент, позволяющий вносить изменения. При вставке элементы контейнера, начиная с указанной позиции до конца вектора смещаются на одну позицию. Если передана некорректная позиция, вставка не происходит, возвращается указатель за последний элемент контейнера. +Метод Erase - принимает позицию (или const int*), удаляет элемент в указанной позиции и возвращает указатель на элемент, который был следующим за удаляемым. Элементы после указанной позиции сдвигаются. Если передана некорректная позиция, удаление не производится. +Метод Clear - очистить вектор. При этом, как правило, изменяется только размер, память очищать не нужно и выполнять релокации тоже. +Метод Resize - принимает размер и значение (по умолчанию 0), изменяет размер массива на заданный. Если переданный размер совпадает с текущим, то без изменений. Если меньше, то изменяет размер. Если больше, то при необходимости производит релокацию и заполняет элементы заданным значением +Метод Reserve - принимает новое значение вместимости, позволяя зарезервировать место в векторе. Если текущий Capacity не меньше переданного, то метод не должен ничего делать. В противном случае выполните релокацию в массив размера заданной вместимости. +Поддержка работы range-based for для контейнера. В данном случае для простоты допустимо возвращать указатели на первый элемент и за последний, концепция итераторов будет обсуждаться позже. +Операторы сравнения на равенство и неравенство, учитывающие размер и поэлементное сравнение элементов +При добавлении элемента, если память, выделенная для вектора, заполнена, то выполните релокацию: выделите массив вдвое большего размера, скопируйте элементы туда, после чего удалите старый массив. В этом случае вместимость должна увеличиться вдвое. Для вектора, сконструированного с помощью конструктора по умолчанию, вместимость == 0, при добавлении элемента вместимость становится 1. + +Примечание +Запрещено использовать стандартные контейнеры (std::vector, умные указатели) +Устройство вектора обсуждалось в конце третьей лекции +Для поддержки range-based for необходимы методы begin, end или внешние функции begin, end, принимающие заданную коллекцию, поэтому допустимо, чтобы они не соответствовали стайлгайду. Если стайлгайд не хочется +Для совместимости с алгоритмами стандартной библиотеки STL может потребоваться swap, ситуация аналогичная, но поскольку требуется внутри класса Swap, достаточно реализовать внешнюю функцию, вызывающую метод Swap контейнера +*/ + +// simple_vector.cpp +#include +#include +#include // для memcpy class SimpleVector { +private: + int* data_; + size_t size_; + size_t capacity_; + +public: + // Конструктор по умолчанию + SimpleVector() : data_(nullptr), size_(0), capacity_(0) {} // пустой указатель + + // Конструктор, принимающий размер вектора и заполняющий его нулями + explicit SimpleVector(size_t size) : data_(new int[size]), size_(size), capacity_(size) { + for (size_t i = 0; i < size_; ++i) data_[i] = 0;// заполнение нулями. + } + + // Конструктор, принимающий размер и значение для заполнения + SimpleVector(size_t size, int data) : data_(new int[size]), size_(size), capacity_(size) { + for (size_t i = 0; i < size_; ++i) data_[i] = data;// заполнение нулями. + } + + // ККонструктор, принимающий список инициализации std::initializer_list, что позволит писать SimpleVector v = {1, 3, 5} + SimpleVector(std::initializer_list init) : data_(new int[init.size()]), size_(init.size()), capacity_(init.size()) { + std::copy(init.begin(), init.end(), data_); // + } + + // Конструктор копирования + SimpleVector(const SimpleVector& obj){ + + if(obj.capacity_ == 0) data_ = nullptr; // проверка, если емкость 0, то nullptr + else data_ = new int[obj.capacity_]; // иначе выделяем место для объекта + + // переписывание данных + size_ = obj.size_; + + capacity_ = obj.capacity_; + + std::copy(obj.data_, obj.data_ + obj.size_, data_); + } + + // Конструктор перемещения + SimpleVector(SimpleVector&& obj) noexcept : data_(obj.data_), size_(obj.size_), capacity_(obj.capacity_) { + // скопировали в списке инициализации, теперь обнуляем исходник + obj.data_ = nullptr; + obj.size_ = 0; + obj.capacity_ = 0; + } + + // Оператор присваивания копированием + SimpleVector& operator=(const SimpleVector& obj) { + if (this != &obj) { // проверка на самого себя + SimpleVector temp(obj); + Swap(temp); + } + return *this; + } + + // Оператор присваивания перемещением + SimpleVector& operator=(SimpleVector&& obj) noexcept { + if (this != &obj) { // проверка на самого себя + delete[] data_; // очистка + data_ = obj.data_; //присвоение + size_ = obj.size_; + capacity_ = obj.capacity_; + + // обнуляем исходник + obj.data_ = nullptr; + obj.size_ = 0; + obj.capacity_ = 0; + } + return *this; + } + + // Деструктор + ~SimpleVector() { delete[] data_; } + + // Метод Swap - принимает другой вектор и меняет содержимое текущего вектора с ним местами + void Swap(SimpleVector& obj) noexcept { + + size_t temp = obj.size_; // переменная для временного хранения size_ и capacity_ + + obj.size_ = size_; + size_ = temp; + + temp = obj.capacity_; + obj.capacity_ = capacity_; + capacity_ = temp; + + int* tempDdata; // переменная для временного хранения data_ + + tempDdata = obj.data_; + obj.data_ = data_; + data_ = tempDdata; + } + + // Операторы индексирования []- позволяет изменять содержимое для неконстантного вектора + int& operator[](size_t i) { + return data_[i]; + } + + const int& operator[](size_t i) const { + return data_[i]; + } + // Метод Size - возвращает число элементов в векторе + size_t Size() const { return size_; } + + // Метод Capacity - возвращает текущее число выделенных ячеек памяти под вектор. + size_t Capacity() const { return capacity_; } + + // Метод Empty - возвращает true, если вектор пуст + bool Empty() const { return size_ == 0; } + + // Метод Data - прямой доступ к памяти, не позволяющий вносить изменения + const int* Data() const { return data_; } + + // Метод PushBack который вставляет элемент в конец вектора. + void PushBack(int value) { + // места может не быть, сначала проверка, есть ли место, .если нет увеличиваем емкость на единицу. + if (size_ == capacity_) { + size_t new_capacity = capacity_ * 2;// т.к. есть требование "В этом случае вместимость должна увеличиться вдвое" + if(new_capacity == 0) new_capacity = 1; + Reserve(new_capacity); + } + data_[size_++] = value; + } + + // Метод PopBack - удаляет последний элемент вектора. При этом изменяется только размер, выделенную память изменять не нужно. + void PopBack() { if (size_ > 0) --size_; } + + // Метод Insert - принимает позицию (или const int*) и элемент, вставляет элемент перед указанной позицией, возвращает указатель на вставленный элемент, позволяющий вносить изменения. При вставке элементы контейнера, начиная с указанной позиции до конца вектора смещаются на одну позицию. Если передана некорректная позиция, вставка не происходит, возвращается указатель за последний элемент контейнера. + int* Insert(const int* pos, int data) { + if ((pos < data_) || (pos > data_ + size_)) return data_ + size_; // проверка, что указатель указатель места вклейки валиден + + size_t i = pos - data_; // расчет индекса начала сдвига + + // возможно Размер совпал с емкостью, + if (size_ == capacity_) { // Тогда надо увеличить емкость на 1 + size_t new_capacity = capacity_ + 1; + Reserve(new_capacity); + } + + // Сдвигаем с элементы после места вклейки вправо + for (size_t j = size_; j > i; --j) data_[j] = data_[j - 1]; + + data_[i] = data; // вклеиваем элемент + ++size_; // помним про то, что надо увеличить размер + + return data_ + i; + } + +// Метод Erase - принимает позицию (или const int*), удаляет элемент в указанной позиции и возвращает указатель на элемент, который был следующим за удаляемым. Элементы после указанной позиции сдвигаются. Если передана некорректная позиция, удаление не производится. + + int* Erase(const int* pos) { + if (pos < data_ || pos >= data_ + size_) return data_ + size_; // проверка, что указатель указатель места вклейки валиден + + size_t i = pos - data_; // расчет индекса начала сдвига + + // Сдвигаем элементы + for (size_t j = i; j < size_ - 1; ++j) data_[j] = data_[j + 1]; + + --size_; // помним про то, что надо увеличить размер + + return data_ + i; + } + + // Метод Clear - очистить вектор. При этом, как правило, изменяется только размер, память очищать не нужно и выполнять релокации тоже. + void Clear() { size_ = 0; } + + // Метод Reserve - принимает новое значение вместимости, позволяя зарезервировать место в векторе. Если текущий Capacity не меньше переданного, то метод не должен ничего делать. В противном случае выполните релокацию в массив размера заданной вместимости. + void Resize(size_t newSize, int data = 0) { + if (newSize == size_) return; // метод не должен ничего делать. + + if (newSize < size_) size_ = newSize; + else { + if (newSize > capacity_) { + Reserve(newSize); + } + + // Заполняем элементы значением data + for (size_t i = size_; i < newSize; ++i) { + data_[i] = data; + } + size_ = newSize; + } + } + + // Метод Reserve + void Reserve(size_t new_capacity) { + if (new_capacity <= capacity_) return; // метод не должен ничего делать. + + int* new_data = new int[new_capacity]; + if (data_) { + std::copy(data_, data_ + size_, new_data); + delete[] data_; + } + data_ = new_data; + capacity_ = new_capacity; + } + + // Поддержка работы range-based for для контейнера. В данном случае для простоты допустимо возвращать указатели на первый элемент и за последний, концепция итераторов будет обсуждаться позже. + int* begin() { return data_; } + + const int* begin() const { return data_; } + + int* end() { return data_ + size_; } + + const int* end() const { return data_ + size_; } + + // Операторы сравнения на равенство и неравенство, учитывающие размер и поэлементное сравнение элементов + bool operator==(const SimpleVector& obj) const { + if (size_ != obj.size_) { + return false; + } + for (size_t i = 0; i < size_; ++i) { + if (data_[i] != obj.data_[i]) { + return false; + } + } + return true; + } + + bool operator!=(const SimpleVector& obj) const { + return !(*this == obj); + } +}; -}; \ No newline at end of file +// Для совместимости с алгоритмами стандартной библиотеки STL может потребоваться swap, ситуация аналогичная, но поскольку требуется внутри класса Swap, достаточно реализовать внешнюю функцию, вызывающую метод Swap контейнера +void swap(SimpleVector& lhs, SimpleVector& rhs) noexcept { + lhs.Swap(rhs); +} diff --git a/05_week/tasks/string_view/string_view.cpp b/05_week/tasks/string_view/string_view.cpp index 438c4536..2df4e4ec 100644 --- a/05_week/tasks/string_view/string_view.cpp +++ b/05_week/tasks/string_view/string_view.cpp @@ -1,7 +1,187 @@ +/* + Представление строки + Необходимо реализовать класс StringView, представляющий упрощенную реализацию std::string_view. В отличие от std::string данный класс не владеет строкой (ресурсами), а хранит лишь размер и указатель на начало строки. Это позволяет быстро создавать представление строки, копировать и создавать подстроки. Важно понимать, что поскольку StringView не владеет строкой, а лишь наблюдает за оригинальной строкой, то при изменении оригинала представление тоже изменится. При этом сам класс StringView не может изменять строку, за которой наблюдает. + + Класс предоставляет следующий функционал: + + Конструктор от std::string, позиции начала подстроки (по умолчанию 0) и длину наблюдаемой подстроки (по умолчанию аналог std::string::npos). Длину подстроки следует обрезать, если она превышает длину строки. Если начало превышает длину, то следует создать StringView с параметрами по умолчанию + Конструктор от C-строки + Конструктор от C-строки и длины + Оператор [] - доступ на чтение символа + Метод Data - возвращает указатель на начало наблюдаемой строки + Метод Front - доступ на чтение к первому символу. + Метод Back - доступ на чтение к последнему символу. + Методы Size, Length - возвращают длину наблюдаемой строки + Метод Empty - проверяет на пустоту + Метод RemovePrefix - убирает заданное количество символов из начала представления + Метод RemoveSuffix - убирает заданное количество символов с конца представления + Метод Substr - может принимать позицию начала поиска и количество элементов и возвращает StringView. В случае, когда подстрока начала поиска превышает длину строки, следует вернуть пустое представление + Метод Find - принимает символ или StringView и позицию начала поиска (по умолчанию 0), возвращает позицию начала совпадения элемента (или аналог std::string::npos) + Метод ToString - создает std::string на основе представления + Примечание + Запрещено использовать std::string_view в реализации + Передавать std::string по значению в первый конструктор очень плохая идея + Получить длину C-строки можно с помощью функции strlen. + Не забудьте про константность методов +*/ + + #include #include class StringView { -}; \ No newline at end of file +private: + const char* data_; + size_t size_; + +public: + inline static const size_t npos = static_cast(-1); + + StringView() : data_(nullptr), size_(0) {} // Конструктор по умолчанию + + +/* + Конструктор от std::string, позиции начала подстроки (по умолчанию 0) и длину наблюдаемой подстроки (по умолчанию аналог std::string::npos). + Длину подстроки следует обрезать, если она превышает длину строки. Если начало превышает длину, то следует создать StringView с параметрами по умолчанию +*/ + StringView(const std::string& str, size_t pos = 0, size_t len = npos) { // -1 т.к. цитата "Константа объявлена как -1. Это связано с тем, что size_t — тип беззнакового целого числа, и -1 — наибольшее возможное представимое значение для этого типа." + if(str.size() <= pos) // проверка, что длина строки больше чем позиция начала. + { + data_ = nullptr; + size_ = 0; + return; + } + + data_ = str.data() + pos; + + size_t maxLen = str.size() - pos; + if ((len == npos) || (len > maxLen)) size_ = maxLen; // вышли, обризаем длину + else size_ = len; + } + +//Конструктор от C-строки + StringView(const char* str){ + if (str == nullptr) { // проверка на пустую строку + data_ = nullptr; + size_ = 0; + return; + } + + data_ = str; + size_ = std::strlen(str); // расчет схдлины строки средством strlen + } + + +//Конструктор от C-строки и длины + StringView(const char* str, size_t len){ + if (str == nullptr) { // проверка на пустую строку + data_ = nullptr; + size_ = 0; + return; + } + // проверки на 0ю длину строки нет т.к. 0 - валидная длина. + data_ = str; + size_ = len; + } + +//Оператор [] - доступ на чтение символа +char operator[](size_t pos) const { return data_[pos]; } + +//Метод Data - возвращает указатель на начало наблюдаемой строки +const char* Data() const { return data_; } + +// Метод Front - доступ на чтение к первому символу. +char Front() const { return data_[0]; } + +// Метод Back - доступ на чтение к последнему символу. +char Back() const { return data_[size_ -1]; } + +// Методы Size, Length - возвращают длину наблюдаемой строки + size_t Size() const { return size_;} + size_t Length() const { return size_; } + +// Метод Empty - проверяет на пустоту + bool Empty() const { + if(size_ == 0) return true; + return false; + } + +// Метод RemovePrefix - убирает заданное количество символов из начала представления + void RemovePrefix(size_t len) { // по идее тут надо проверить кооректность, а именно что длина должна быть меньше size_ len + if (len >= size_) { + data_ = nullptr; + size_ = 0; + } + else { + data_ += len; + size_ -= len; + } + } + +// Метод RemoveSuffix - убирает заданное количество символов с конца представления + void RemoveSuffix(size_t len) { + if (len >= size_) { + data_ = nullptr; + size_ = 0; + } + else size_ -= len; + + } + +// Метод Substr - может принимать позицию начала поиска и количество элементов и возвращает StringView. В случае, когда подстрока начала поиска превышает длину строки, следует вернуть пустое представление + StringView Substr(size_t pos = 0, size_t len = npos) const { + if (pos >= size_) { return StringView(); } // В случае, когда подстрока начала поиска превышает длину строки, следует вернуть пустое представление + + size_t newSize = size_ - pos; + if ((len != npos) && (len < newSize)) newSize = len; + + return StringView(data_ + pos, newSize); + } +// Метод Find - принимает символ или StringView и позицию начала поиска (по умолчанию 0), возвращает позицию начала совпадения элемента (или аналог std::string::npos) + + size_t Find(char symb, size_t pos = 0) const { + + for (size_t i = pos; i < size_; ++i) { + if (data_[i] == symb) return i; + } + + + return npos; //не совпало + } +// переопределение Find ддля строки + size_t Find(const StringView& str, size_t pos = 0) const { + + + // Проверка пустого указателя + if (str.Empty()) return (pos <= size_) ? pos : npos; + + // Проверка границы и позиции + if ((pos >= size_) || ((pos + str.size_) >= size_)) return npos; + + size_t i = pos; // индекс проверяемой позиции + size_t j = 0; // счетчик совпадений + + while(i < size_) // првоеряемая должна быть меньше размера строки + { + if(str.data_[j] == data_[i]) j++; // элементы совпали, инкриментируем счетчик совпадений + else j = 0; + + if(j == str.size_) return i-j+1; // длина строки совпала с количеством совпадений, значит нашли совпадение + + i++; + } + + return npos; + } + + // Преобразование в std::string + std::string ToString() const { + if (data_ == nullptr || size_ == 0) { + return std::string(); + } + return std::string(data_, size_); + } + +}; diff --git a/05_week/tasks/tracer/tracer.cpp b/05_week/tasks/tracer/tracer.cpp index 2ccfb417..bc509f0d 100644 --- a/05_week/tasks/tracer/tracer.cpp +++ b/05_week/tasks/tracer/tracer.cpp @@ -1,6 +1,134 @@ +/* + Трассировщик +Необходимо реализовать класс Tracer, подсчитывающий вызовы своих конструкторов, операторов присваивания и деструктора. + +В качестве данных внутри класса должна храниться имя std::string и идентификатор. + +В классе присутствуют следующие счетчики, доступные извне: + +count - общее количество созданных объектов (используется для генерации id) +default_ctor - количество вызовов конструктора по умолчанию +str_ctor - количество вызовов конструктора от строки +copy_ctor - количество вызовов конструктора копирования +move_ctor - количество вызовов конструктора перемещения +copy_assign - количество вызовов оператора копирующего присваивания +move_assign - количество вызовов оператора перемещающего присваивания +dtor - количество вызовов деструктора +alive - количество живых объектов в данный момент +Правила изменения идентификатора (id): + +присваивается при создании и НЕ меняется в течение жизни объекта +каждый новый объект получает уникальный id (увеличивающийся счетчик) +при использовании операторов не изменяется +Класс предоставляет следующий функционал: + +Конструктор по умолчанию - создает объект с именем obj_{id} ("obj_1") +Конструктор от строки std::string - создает объект с именем {name}_{id} +Конструктор копирования - копирует имя, но создает id +Конструктор перемещения - перемещает имя, но создает id +Оператор присваивания копированием - копирует имя, не изменяет id +Оператор перемещения копированием - перемещает имя, не изменяет id +Деструктор - изменяет свои счетчики +Метод Id - возвращает идентификатор объекта +Метод Name - возвращает константную ссылку на имя. +Метод Data - возвращает указатель на данные строки. +Метод ResetStats - сбрасывает на 0 все счетчики +Примечание +Для удобства отладки можно написать функции, выводящие на экран статистики и логирующие действия. +*/ + #include + class Tracer { +private: + size_t id_; + std::string name_; + + +public: + // static для переменных, тогда при вызове значение будет известно предыдущее значение. + static size_t count; //- общее количество созданных объектов (используется для генерации id) + static size_t default_ctor; // - количество вызовов конструктора по умолчанию + static size_t str_ctor; // - количество вызовов конструктора от строки + static size_t copy_ctor; // - количество вызовов конструктора копирования + static size_t move_ctor; // - количество вызовов конструктора перемещения + static size_t copy_assign; // - количество вызовов оператора копирующего присваивания + static size_t move_assign; // - количество вызовов оператора перемещающего присваивания + static size_t dtor; // - количество вызовов деструктора + static size_t alive; // - количество живых объектов в данный момент + + Tracer(): id_(++count), name_ ("obj_" + (std::to_string(count))){ + ++default_ctor; + ++alive; + } + + Tracer(const std::string data) : id_(++count), name_ (data +"_" + (std::to_string(count))){ + ++str_ctor; + ++alive; + } + + Tracer(const Tracer& data) : id_(++count), name_(data.name_) { + ++copy_ctor; + ++alive; + } + + Tracer(Tracer&& data) noexcept : id_(++count), name_(std::move(data.name_)){ + ++move_ctor; + ++alive; + } + + Tracer& operator=(const Tracer& data) { // копирует имя, не изменяет id + if (this != &data) { + name_ = data.name_; + ++copy_assign; + } + return *this; + } + + Tracer& operator=(Tracer&& data) { // перемещает имя, не изменяет id + if (this != &data) { + name_ = std::move(data.name_); + ++move_assign; + } + return *this; + } + + // Деструктор + ~Tracer() { + ++dtor; + --alive; + } + + // Методы доступа + size_t Id() const { return id_; } + const std::string& Name() const { return name_; } + const char* Data() const { return name_.c_str(); } + + static void ResetStats() { + count = 0; + default_ctor = 0; + str_ctor = 0; + copy_ctor = 0; + move_ctor = 0; + copy_assign = 0; + move_assign = 0; + dtor = 0; + alive = 0; + } + + +}; + -}; \ No newline at end of file +// Инициализация статических членов класса +size_t Tracer::count = 0; +size_t Tracer::default_ctor = 0; +size_t Tracer::str_ctor = 0; +size_t Tracer::copy_ctor = 0; +size_t Tracer::move_ctor = 0; +size_t Tracer::copy_assign = 0; +size_t Tracer::move_assign = 0; +size_t Tracer::dtor = 0; +size_t Tracer::alive = 0; diff --git a/06_week/tasks/smart_ptr/smart_ptr.cpp b/06_week/tasks/smart_ptr/smart_ptr.cpp index 05de4dc8..98345916 100644 --- a/06_week/tasks/smart_ptr/smart_ptr.cpp +++ b/06_week/tasks/smart_ptr/smart_ptr.cpp @@ -1,10 +1,374 @@ + +/* +Умные указатели + +Необходимо реализовать классы SharedPtr и WeakPtr, которые представляют собой аналоги std::shared_ptr и std::weak_ptr. То есть SharedPtr реализует семантику разделяемого владения, а WeakPtr представляет наблюдателя. В качестве объекта владения выступает строка std::string. + +Класс SharedPtr предоставляет следующий функционал: + + Все необходимые конструкторы умного указателя с уникальным владением + Операторы, реализующие поведение указателей + Метод Get - получить сырой указатель + Метод Reset - удаляет старый объект и принимает владение новым + Метод Swap - обменивается владеющими указателями с другим SharedPtr + Метод UseCount - возвращает количество владельцев + Оператор bool() - приводит указатель к типу bool + +Класс WeakPtr предоставляет следующий функционал: + + Все необходимые конструкторы умного указателя с уникальным владением + Операторы, реализующие поведение указателей + Метод Reset - убирает ссылку на владеющий объект + Метод Swap - обменивается владеющими указателями с другим WeakPtr + Метод UseCount - возвращает количество владельцев + Метод Expired - проверяет, истекло ли время жизни исходного объекта + Метод Lock - создает владеющий указатель SharedPtr на объект, если он ещё жив + +Необходимо реализовать: + + Функию MakeShared - принимает строку и возвращает умный указатель SharedPtr. Функция должна поддерживать как копирование, так и перемещение принимаемого объекта. + Функцию Swap - для обмена умными указателями SharedPtr, и указателями WeakPtr + +Примечание + + Запрещено использовать умные указатели STL в реализации + Одна из задач, где может пригодиться ключевое слово friend, но желательно им не злоупотреблять + Счетчики допустимо использовать обычные, не атомарные + Метод UseCount класса WeakPtr возвращает именно владельцев, поскольку именно владение определяет время жизни объекта + + +*/ + + #include +// Forward declarations +class SharedPtr; +class WeakPtr; class SharedPtr { +private: + std::string* ptr_; // Указатель + size_t* sharedPtrCount_; // Счетчик сильных ссылок + size_t* weakPtrCount_; // Счетчик слабых ссылок + + + +public: + // Все необходимые конструкторы умного указателя с уникальным владением + SharedPtr() : ptr_(nullptr), sharedPtrCount_(nullptr), weakPtrCount_(nullptr) {} + explicit SharedPtr(std::string* p) : ptr_(p) { + if (p != nullptr) { + sharedPtrCount_ = new size_t(1); + weakPtrCount_ = new size_t(0); + } else { + sharedPtrCount_ = nullptr; + weakPtrCount_ = nullptr; + } + } + + SharedPtr(const SharedPtr& data) : ptr_(data.ptr_), sharedPtrCount_(data.sharedPtrCount_), weakPtrCount_(data.weakPtrCount_) { + if (sharedPtrCount_ != nullptr) (*sharedPtrCount_)++; + } + + SharedPtr(SharedPtr&& data) noexcept : ptr_(data.ptr_), sharedPtrCount_(data.sharedPtrCount_), weakPtrCount_(data.weakPtrCount_) { + data.ptr_ = nullptr; + data.sharedPtrCount_ = nullptr; + data.weakPtrCount_ = nullptr; + } + + // Destructor + ~SharedPtr() { + Reset(); + } + +// Операторы, реализующие поведение указателей + // Присвоение (перемещение) + SharedPtr& operator=(const SharedPtr& data) { + if (this != &data) { + Reset(); // очистка + // присвоение новых значений + ptr_ = data.ptr_; + + sharedPtrCount_ = data.sharedPtrCount_; + weakPtrCount_ = data.weakPtrCount_; + if (sharedPtrCount_) { + (*sharedPtrCount_)++; + } + } + return *this; + } + + + SharedPtr& operator=(SharedPtr&& data) noexcept { + if (this != &data) { + Reset(); // очистка Метод UseCount - возвращает количество владельцев + ptr_ = data.ptr_; + // присвоение новых значений и обнуление data + sharedPtrCount_ = data.sharedPtrCount_; + weakPtrCount_ = data.weakPtrCount_; + data.ptr_ = nullptr; + data.sharedPtrCount_ = nullptr; + data.weakPtrCount_ = nullptr; + } + return *this; + } + + // укозатели + std::string& operator*() const { return *ptr_; } + std::string* operator->() const { return ptr_; } + + +// Методы + // Метод Get - получить сырой указатель + std::string* Get() const { return ptr_; } + + // Метод Reset - удаляет объект + void Reset() { + if (sharedPtrCount_ != nullptr) { // Есть, что очищать? + (*sharedPtrCount_)--; + + if (*sharedPtrCount_ == 0) { + delete ptr_; + ptr_ = nullptr; + + if (*weakPtrCount_ == 0) { + delete sharedPtrCount_; + delete weakPtrCount_; + } + } + + sharedPtrCount_ = nullptr; + weakPtrCount_ = nullptr; + } + ptr_ = nullptr; + + + } + + // Метод Reset - удаляет старый объект и принимает владение новым + void Reset(std::string* p) { + Reset(); // сброс + + // Присвоение + if (p) { // а есть что присвоить? + ptr_ = p; + sharedPtrCount_ = new size_t(1); + weakPtrCount_ = new size_t(0); + } + } + + // Метод Swap - обменивается владеющими указателями с другим WeakPtr + void Swap(SharedPtr& data) { + std::string* tempPtr; + size_t* temp; + + // Обмен указателями + tempPtr = data.ptr_; + data.ptr_ = ptr_; + ptr_ = tempPtr; + + + // Обмен счетчиком слабых ссылок + temp = data.sharedPtrCount_; + data.sharedPtrCount_ = sharedPtrCount_; + sharedPtrCount_ = temp; + + // Обмен счетчиком сильных ссылок + temp = data.weakPtrCount_; + data.weakPtrCount_ = weakPtrCount_; + weakPtrCount_ = temp; + } + + // Метод UseCount - возвращает количество владельцев + size_t UseCount() const { + if(sharedPtrCount_ != nullptr) return *sharedPtrCount_; + return 0; + } + + // Оператор bool() - приводит указатель к типу bool + operator bool() const { + if(ptr_ != nullptr) return true; + return false; + } + + // Friend classes + friend class WeakPtr; + friend void Swap(SharedPtr& lhs, SharedPtr& rhs); }; class WeakPtr { +private: + std::string* ptr_; // Указатель + size_t* sharedPtrCount_; // Счетчик сильных ссылок + size_t* weakPtrCount_; // Счетчик слабых ссылок + +public: + // Все необходимые конструкторы умного указателя с уникальным владением + // конструктор по умолчанию + WeakPtr() : ptr_(nullptr), sharedPtrCount_(nullptr), weakPtrCount_(nullptr) {} + + // конструктор от SharedPtr + WeakPtr(const SharedPtr& sp) : ptr_(sp.ptr_), sharedPtrCount_(sp.sharedPtrCount_), weakPtrCount_(sp.weakPtrCount_) { + if (weakPtrCount_) { + (*weakPtrCount_)++; + } + } + + // Конструктор копирования + WeakPtr(const WeakPtr& data) : ptr_(data.ptr_), sharedPtrCount_(data.sharedPtrCount_), weakPtrCount_(data.weakPtrCount_) { + if (weakPtrCount_) { + (*weakPtrCount_)++; + } + } + + // Конструктор перемещения + WeakPtr(WeakPtr&& data) noexcept : ptr_(data.ptr_), sharedPtrCount_(data.sharedPtrCount_), weakPtrCount_(data.weakPtrCount_) { + // Очищаем данные из которых переместили + data.ptr_ = nullptr; + data.sharedPtrCount_ = nullptr; + data.weakPtrCount_ = nullptr; + } + + // Диструктор + ~WeakPtr() { + Reset(); + } + + + WeakPtr& operator=(const WeakPtr& data) { + if (this != &data) { // текущий равен ? + // нет -присваивание. + Reset(); // очистка + ptr_ = data.ptr_; + sharedPtrCount_ = data.sharedPtrCount_; + weakPtrCount_ = data.weakPtrCount_; + if (weakPtrCount_) (*weakPtrCount_)++; + } + return *this; + } + + + WeakPtr& operator=(WeakPtr&& data) noexcept { + // аналогично как выше, но + очистка data + if (this != &data) { + Reset(); + ptr_ = data.ptr_; + sharedPtrCount_ = data.sharedPtrCount_; + weakPtrCount_ = data.weakPtrCount_; + // очистка data + data.ptr_ = nullptr; + data.sharedPtrCount_ = nullptr; + data.weakPtrCount_ = nullptr; + } + return *this; + } + + + WeakPtr& operator=(const SharedPtr& sp) { + // аналогично как выше + Reset(); + ptr_ = sp.ptr_; + sharedPtrCount_ = sp.sharedPtrCount_; + weakPtrCount_ = sp.weakPtrCount_; + if (weakPtrCount_) { + (*weakPtrCount_)++; + } + return *this; + } + + +// Методы + // Метод Reset - убирает ссылку на владеющий объект + void Reset() { + if (weakPtrCount_ != nullptr) { + (*weakPtrCount_ )--; + + if ((*sharedPtrCount_ == 0) && (*weakPtrCount_ == 0)) { + delete sharedPtrCount_; + delete weakPtrCount_; + } + + // очистка this + weakPtrCount_ = nullptr; + sharedPtrCount_ = nullptr; + ptr_ = nullptr; + } + } + + // Метод Swap - обменивается владеющими указателями с другим WeakPtr + void Swap(WeakPtr& data) { + std::string* tempPtr; + size_t* temp; + + // Обмен указателями + tempPtr = data.ptr_; + data.ptr_ = ptr_; + ptr_ = tempPtr; + + + // Обмен счетчиком слабых ссылок + temp = data.sharedPtrCount_; + data.sharedPtrCount_ = sharedPtrCount_; + sharedPtrCount_ = temp; + + // Обмен счетчиком сильных ссылок + temp = data.weakPtrCount_; + data.weakPtrCount_ = weakPtrCount_; + weakPtrCount_ = temp; + } + + + // Метод UseCount - возвращает количество владельцев + size_t UseCount() const { + if(sharedPtrCount_ != nullptr) return *sharedPtrCount_; + return 0; + } + + // Метод Expired - проверяет, истекло ли время жизни исходного объекта + bool Expired() const { + if(sharedPtrCount_ == nullptr) return true; // указатель на nullptr значит истекло время жизни + if(*sharedPtrCount_ != 0) return false; // счетчик не 0 значит не истекло время жизни + return true; // счетчик = 0 значит истекло время жизни + } + + // Метод Lock - создает владеющий указатель SharedPtr на объект, если он ещё жив + SharedPtr Lock() const { + if (!Expired()) { + SharedPtr sp; + sp.ptr_ = ptr_; + sp.sharedPtrCount_ = sharedPtrCount_; + sp.weakPtrCount_ = weakPtrCount_; + if (sp.sharedPtrCount_ != nullptr) { + (*sp.sharedPtrCount_)++; + } + return sp; + } + return SharedPtr(); + } + + // Friend functions + friend void Swap(WeakPtr& lhs, WeakPtr& rhs); +}; + + + + +// Функия MakeShared - принимает строку и возвращает умный указатель SharedPtr. Функция должна поддерживать как копирование, так и перемещение принимаемого объекта. +SharedPtr MakeShared(const std::string& str) { + return SharedPtr(new std::string(str)); +} + +SharedPtr MakeShared(std::string&& str) { + return SharedPtr(new std::string(std::move(str))); +} + +// Функция Swap - для обмена умными указателями SharedPtr, и указателями WeakPtr +void Swap(SharedPtr& dataA, SharedPtr& dataB) { + dataA.Swap(dataB); +} -}; \ No newline at end of file +void Swap(WeakPtr& dataA, WeakPtr& dataB) { + dataA.Swap(dataB); +} diff --git a/06_week/tasks/unique_ptr/unique_ptr.cpp b/06_week/tasks/unique_ptr/unique_ptr.cpp index db8729ad..ffa94622 100644 --- a/06_week/tasks/unique_ptr/unique_ptr.cpp +++ b/06_week/tasks/unique_ptr/unique_ptr.cpp @@ -1,6 +1,119 @@ #include -class UniquePtr { +/*Умный указатель уникального владения + +Необходимо реализовать класс UniquePtr, который реализует семантику уникального владения строкой std::string. Класс соответствует умному указателю std::unique_ptr. + +Класс предоставляет следующий функционал: + + Все необходимые конструкторы умного указателя с уникальным владением + Операторы, реализующие поведение указателей + Метод Get - получить сырой указатель + Метод Release - освобождает владение (возвращает указатель и становится nullptr) + Метод Reset - удаляет старый объект и принимает владение новым + Метод Swap - обменивается владеющими указателями с другим UniquePtr + Оператор bool() - приводит указатель к типу bool + +Необходимо реализовать: + + Функию MakeUnique - принимает строку и возвращает умный указатель UniquePtr. Функция должна поддерживать как копирование, так и перемещение принимаемого объекта + Функцию Swap - для обмена умными указателями UniquePtr + +Примечание + + Запрещено использовать умные указатели STL в реализации +*/ + + + +class UniquePtr{ +private: + std::string* ptr_; + +public: + // Все необходимые конструкторы умного указателя с уникальным владением + UniquePtr(): ptr_(nullptr) {} + UniquePtr(std::string* ptr) : ptr_(ptr){} + UniquePtr(UniquePtr&& ptrA) noexcept : ptr_(ptrA.ptr_) { ptrA.ptr_ = nullptr;} + + + // Оператор перемещения + UniquePtr& operator=(UniquePtr&& ptrA) noexcept { + if (this != &ptrA) { + delete ptr_; + ptr_ = ptrA.ptr_; + ptrA.ptr_ = nullptr; + } + return *this; + } + + // Деструктор + ~UniquePtr() { delete ptr_; } + + std::string& operator*() const { return *ptr_; } + std::string* operator->() const { return ptr_; } + + // Получить сырой указатель + std::string* Get() const { return ptr_; } + + + // Метод Release - освобождает владение (возвращает указатель и становится nullptr) + std::string* Release() { + std::string* temp; + temp = ptr_; + ptr_ = nullptr; + return temp; + } + + + // Метод Reset - удаляет старый объект и принимает владение новым + void Reset(std::string* ptr = nullptr) { + delete ptr_; + ptr_ = ptr; + } + + + /* + // Метод Swap - обменивается владеющими указателями с другим UniquePtr + void Swap(UniquePtr& ptrA) noexcept { + std::string* temp = ptrA.ptr_; + ptrA.ptr_ = this->ptr_; + this->ptr_ = temp; + } + */ + // Обменяться указателями (без использования библиотечных функций) + void Swap(UniquePtr& other) noexcept { + std::string* temp = this->ptr_; + this->ptr_ = other.ptr_; + other.ptr_ = temp; + } + + // Оператор bool() - приводит указатель к типу bool + explicit operator bool() const { return ptr_ != nullptr;} + + + // Запрет копирования + UniquePtr(const UniquePtr&) = delete; + UniquePtr& operator=(const UniquePtr&) = delete; + +}; + + +//Необходимо реализовать: + +// Функию MakeUnique - принимает строку и возвращает умный указатель UniquePtr. Функция должна поддерживать как копирование, так и перемещение принимаемого объекта + +UniquePtr MakeUnique(const std::string& data); +UniquePtr MakeUnique(std::string&& data); + +UniquePtr MakeUnique(const std::string& data) { return UniquePtr(new std::string(data)); } + +UniquePtr MakeUnique(std::string&& data) { return UniquePtr(new std::string(std::move(data))); } + + +// Функцию Swap - для обмена умными указателями UniquePtr +void Swap(UniquePtr& dataA, UniquePtr& dataB) noexcept; + +void Swap(UniquePtr& dataA, UniquePtr& dataB) noexcept {dataA.Swap(dataB); } -}; \ No newline at end of file diff --git a/07_week/tasks/array/array.cpp b/07_week/tasks/array/array.cpp index b1b8feab..99b962d4 100644 --- a/07_week/tasks/array/array.cpp +++ b/07_week/tasks/array/array.cpp @@ -1,6 +1,255 @@ #include +/* +Массив на стеке +Необходимо реализовать класс Array, представляющий реализацию массива на стеке для элементов произвольного типа. + +Класс предоставляет следующий функционал: + + Конструктор по умолчанию + Конструктор, принимающий список инициализации std::initializer_list + Конструктор копирования + Конструктор перемещения + Операторы присваивания копированием + Оператор присваивания перемещением + Деструктор + Операторы индексирования []- обеспечивают доступ по ссылке к элементу контейнера + Метод Front - возвращает ссылку на первый элемент массива, для пустого массива UB + Метод Back - возвращает ссылку на последний элемент массива, для пустого массива UB + Метод Data - прямой доступ к памяти, для константного контейнера изменять данные нельзя + Метод Empty - возвращает true, если контейнер пуст + Метод Size - возвращает размер контейнера + Метод Fill - заполняет контейнер определенным элементом + Метод Swap - обменивается содержимым с другим аналогичным контейнером. + Поддержка работы range-based for для контейнера. В данном случае для простоты допустимо возвращать указатели на первый элемент и за последний, концепция итераторов будет обсуждаться позже. Необходимо реализовать методы begin, end + Методы cbegin, cend - возвращающие указатель на соответствующий константный элемент + +Внешние функции: + + Операторы сравнения на равенство и неравенство + Операторы сравнения на больше, меньше, больше или равно, меньше или равно, производящие лексикографическое сравнение + Функция swap - обменивается содержимым с другим аналогичным контейнером. + Функция get - обращение к элементу аналогично кортежу. Имеет дополнительный параметр шаблона I для порядкового номера элемента. Может работать с временным массивом. Позволяет изменять элементы для неконстантного массива. + +Примечание + + Запрещено использовать стандартные контейнеры (std::vector, std::array, ...) + Даже если используются default конструкторы, генерируемые компилятором, необходимо явно их создать + Некоторые методы могут иметь константные и неконстантные версии + Для поддержки range-based for необходимы методы begin, end или внешние функции begin, end, принимающие заданную коллекцию, поэтому допустимо, чтобы они не соответствовали стайлгайду. Если не хочется нарушать стайлгайд, то методы класса могут быть с большой буквы, но внешние функции должны быть с маленькой + Для совместимости с алгоритмами стандартной библиотеки STL может потребоваться swap, ситуация аналогичная, но поскольку требуется внутри класса Swap, достаточно реализовать внешнюю функцию, вызывающую метод Swap контейнера + + +*/ + + + +// для работы с любым типом данных создаем шаблон. далее он будет заменен на нужный тип при компиляции кода с конкретным применением класса +template class Array { +private: + T data_[N]; // Массив данных пока не определенного типа вернее типа определяемого из контекста в момент компиляции. + +public: + + // Конструктор по умолчанию, default - значит за меня его соберет компилятор. На данном этапе тип данных с которым работаем не известен. + Array() = default; + + // Конструктор, принимающий список инициализации std::initializer_list + Array(std::initializer_list list) { + size_t i = 0; + for (auto pos = list.begin(); pos != list.end() && i < N; ++pos, ++i) data_[i] = *pos; + // исп. auto т.к. для автоматического определения типа данных. + // Проходим от начала и до конца запоминая значения. + } + + // Конструктор копирования + Array(const Array& data) { + for (std::size_t i = 0; i < N; ++i) data_[i] = data.data_[i]; // копирование всех элементов + } + + // Конструктор перемещения + Array(Array&& data) noexcept { // такое было в предыдущих заданиях + for (std::size_t i = 0; i < N; ++i) data_[i] = std::move(data.data_[i]); + } + + // Операторы присваивания копированием + Array& operator=(const Array& data) { + if (this != &data) { // првоерка, что не копируем сами себя. + for (std::size_t i = 0; i < N; ++i) data_[i] = data.data_[i]; // копируем + } + // иначе смысла копировать нет. + return *this; + } + + // Оператор присваивания перемещением + Array& operator=(Array&& data) noexcept { + if (this != &data) { // првоерка, что не копируем сами себя. + for (std::size_t i = 0; i < N; ++i) data_[i] = std::move(data.data_[i]); + } + return *this; + } + +// Деструктор + ~Array() = default; // диструктор по умолчанию, default - значит за меня его соберет компилятор. На данном этапе тип данных с которым работаем не известен. + + // Операторы индексирования []- обеспечивают доступ по ссылке к элементу контейнера + T& operator[](std::size_t i) { + return data_[i]; + } + + // перегрузка для const + const T& operator[](std::size_t index) const { + return data_[index]; + } + +/*---- МЕТОДЫ----*/ + + // Метод Front - возвращает ссылку на первый элемент массива, для пустого массива UB + T& Front() { return data_[0]; } + + // перегрузка для const + const T& Front() const { return data_[0]; } + + // Доступ к последнему элементу + T& Back() { return data_[N - 1];} + + +// Метод Back - возвращает ссылку на последний элемент массива, для пустого массива UB + const T& Back() const { return data_[N - 1];} + +// Метод Data - прямой доступ к памяти, для константного контейнера изменять данные нельзя + T* Data() { return data_;} // для константного контейнера изменять данные нельзя + const T* Data() const { return data_; } + +// Метод Empty - возвращает true, если контейнер пуст + bool Empty() const { return N == 0; } + +// Метод Size - возвращает размер контейнера + std::size_t Size() const { return N; } + +// Метод Fill - заполняет контейнер определенным элементом + void Fill(const T& data) { + for (std::size_t i = 0; i < N; ++i) data_[i] = data; + } + +// Метод Swap - обменивается содержимым с другим аналогичным контейнером. + void Swap(Array& data) noexcept { + for (std::size_t i = 0; i < N; ++i) std::swap(data_[i], data.data_[i]); + } + +/* + Поддержка работы range-based for для контейнера. В данном случае для простоты допустимо возвращать указатели на первый элемент и за последний, концепция итераторов будет обсуждаться позже. Необходимо реализовать методы begin, end + Методы cbegin, cend - возвращающие указатель на соответствующий константный элемент +*/ + + // Итераторы для range-based for + T* begin() { return data_; } + + // перегрузка для const + const T* begin() const { return data_; } + + T* end() { return data_ + N; } + + const T* end() const { return data_ + N; } + + // Константные итераторы + const T* cbegin() const { return data_; } + + const T* cend() const { return data_ + N; } +}; + + +/* + + Операторы сравнения на равенство и неравенство + Операторы сравнения на больше, меньше, больше или равно, меньше или равно, производящие лексикографическое сравнение + Функция swap - обменивается содержимым с другим аналогичным контейнером. + Функция get - обращение к элементу аналогично кортежу. Имеет дополнительный параметр шаблона I для порядкового номера элемента. Может работать с временным массивом. Позволяет изменять элементы для неконстантного массива. + +*/ + + +// Операторы сравнения на равенство и неравенство + template + // равенство - первое базовое сравнение + bool operator==(const Array& dataA, const Array& dataB) { // равенство + for (std::size_t i = 0; i < N; ++i) if (dataA[i] != dataB[i]) return false; + // Сравнили все и все совпало. + return true; + } + + template + bool operator!=(const Array& dataA, const Array& dataB) { + return !(dataA == dataB); // идем от обратного + } + +// Операторы сравнения на больше, меньше, больше или равно, меньше или равно, производящие лексикографическое сравнение +// + template + bool operator > (const Array& dataA, const Array& dataB) { // меньше + for (std::size_t i = 0; i < N; ++i) { + if (dataA[i] > dataB[i]) return true; // значение больше, возвращаем true + if (dataA[i] < dataB[i]) return false; // значение меньше, возвращаем false + } + + // значения равны, возвращаем false + return false; + } + + // А < В это аналогично В > А, оператор ">" определен выше + template + bool operator < (const Array& dataA, const Array& dataB) { + return dataB > dataA; + } + + // А <= В это аналогично !(А > В), оператор ">" определен выше + template + bool operator <= (const Array& dataA, const Array& dataB) { + return !(dataA > dataB); // идем от обратного + } + + +// А >= В это аналогично !(А < В), оператор "<" определен выше +template +bool operator >= (const Array& dataA, const Array& dataB) { + return !(dataA < dataB); +} + +// Функция swap - обменивается содержимым с другим аналогичным контейнером. +template +void swap(Array& dataA, Array& dataB) noexcept { + dataA.Swap(dataB); +} + + + +// Функция get - обращение к элементу аналогично кортежу. Имеет дополнительный параметр шаблона I для порядкового номера элемента. Может работать с временным массивом. Позволяет изменять элементы для неконстантного массива. +template +struct GetHelper { + static T& get(Array& arr) { return arr[I]; } + + static const T& get(const Array& arr) { return arr[I]; } + + static T&& get(Array&& arr) { return std::move(arr[I]); } +}; + +// Функция get для доступа по индексу массива +template +auto get(Array& arr) -> decltype(GetHelper::get(arr)) { + + return GetHelper::get(arr); +} + +template +auto get(const Array& arr) -> decltype(GetHelper::get(arr)) { + + return GetHelper::get(arr); +} -}; \ No newline at end of file +template +auto get(Array&& arr) -> decltype(GetHelper::get(std::move(arr))) { + + return GetHelper::get(std::move(arr)); +} diff --git a/07_week/tasks/make_unique/make_unique.cpp b/07_week/tasks/make_unique/make_unique.cpp index e0fdcbd7..5b6ff19b 100644 --- a/07_week/tasks/make_unique/make_unique.cpp +++ b/07_week/tasks/make_unique/make_unique.cpp @@ -1,4 +1,16 @@ #include - - -/* return_type */ MakeUnique( /* args */ ); +/* +* +* Создаем умный указатель std::unique_ptr на новый объект типа T +* где: +* T Тип создаваемого объекта +* data- типы аргументов для конструктора +* x - аргументы для конструктора объекта типа T +* std::unique_ptr - умный указатель, владеющий созданным объектом. Важно !! без этого не пройти тесты или надо комментировать строку 133 и 134 т.к. там тест конкретно ждет * std::unique_ptr +* + */ +template +std::unique_ptr MakeUnique(Data&&... x) { + // Выделяем память и конструируем объект + return std::unique_ptr(new T(std::forward(x)...)); +}