diff --git a/01_week/tasks/addition/addition.cpp b/01_week/tasks/addition/addition.cpp index 92872802..29b7c4e6 100644 --- a/01_week/tasks/addition/addition.cpp +++ b/01_week/tasks/addition/addition.cpp @@ -1,7 +1,8 @@ #include #include - -int64_t Addition(int a, int b) { - throw std::runtime_error{"Not implemented"}; +int64_t Addition(int a, int b) +{ + // throw std::runtime_error{"Not implemented"}; + return static_cast(a) + b; } \ No newline at end of file diff --git a/01_week/tasks/char_changer/char_changer.cpp b/01_week/tasks/char_changer/char_changer.cpp index 3a7344d9..06087aa1 100644 --- a/01_week/tasks/char_changer/char_changer.cpp +++ b/01_week/tasks/char_changer/char_changer.cpp @@ -1,7 +1,111 @@ #include #include +#include +size_t CharChanger(char array[], size_t size, char delimiter = ' ') +{ + // throw std::runtime_error{"Not implemented"}; + size_t j = 0; + size_t i = 0; -size_t CharChanger(char array[], size_t size, char delimiter = ' ') { - throw std::runtime_error{"Not implemented"}; + while (i < size && array[i] != '\0') + { + if (array[i] == ' ') + { + // Пропускаем все пробелы + size_t spaces = 0; + while (i < size && array[i] == ' ') + { + i++; + spaces++; + } + + // Записываем разделитель если есть пробелы + if (spaces > 0) + { + if (j < size - 1) + { + array[j] = delimiter; + j++; + } + else + { + break; + } + } + } + else + { + // Определяем длину последовательности одинаковых символов + char original = array[i]; + size_t count = 0; + while (i + count < size && array[i + count] == original && array[i + count] != '\0') + { + count++; + } + + // Преобразуем символ + char current = original; + if (std::isdigit(static_cast(original))) + { + current = '*'; + } + else if (std::islower(static_cast(original))) + { + current = std::toupper(static_cast(original)); + } + else if (!std::isupper(static_cast(original))) + { + current = '_'; + } + + // Записываем результат с учетом правила повторений + if (count == 1) + { + if (j < size - 1) + { + array[j] = current; + j++; + } + else + { + break; + } + } + else + { + if (j + 1 < size - 1) + { + array[j] = current; + if (count >= 10) + { + array[j + 1] = '0'; + } + else + { + array[j + 1] = '0' + count; + } + j += 2; + } + else + { + break; + } + } + + i += count; + } + } + + // Завершаем строку + if (j < size) + { + array[j] = '\0'; + } + else if (size > 0) + { + array[size - 1] = '\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..e1aa25a0 100644 --- a/01_week/tasks/check_flags/check_flags.cpp +++ b/01_week/tasks/check_flags/check_flags.cpp @@ -1,8 +1,10 @@ #include #include +#include +#include - -enum class CheckFlags : uint8_t { +enum class CheckFlags : uint8_t +{ NONE = 0, TIME = (1 << 0), DATE = (1 << 1), @@ -13,6 +15,54 @@ enum class CheckFlags : uint8_t { ALL = TIME | DATE | USER | CERT | KEYS | DEST }; -void PrintCheckFlags(CheckFlags flags) { - throw std::runtime_error{"Not implemented"}; +void PrintCheckFlags(CheckFlags flags) +{ + // throw std::runtime_error{"Not implemented"}; + uint8_t value = static_cast(flags); + + // Проверка на выход за диапазон + if (value > static_cast(CheckFlags::ALL)) + { + return; + } + + std::vector active_flags; + + // Проверяем каждый флаг в правильном порядке + if (value & static_cast(CheckFlags::TIME)) + { + active_flags.push_back("TIME"); + } + if (value & static_cast(CheckFlags::DATE)) + { + active_flags.push_back("DATE"); + } + if (value & static_cast(CheckFlags::USER)) + { + active_flags.push_back("USER"); + } + if (value & static_cast(CheckFlags::CERT)) + { + active_flags.push_back("CERT"); + } + if (value & static_cast(CheckFlags::KEYS)) + { + active_flags.push_back("KEYS"); + } + if (value & static_cast(CheckFlags::DEST)) + { + active_flags.push_back("DEST"); + } + + // Формируем вывод + std::cout << "["; + for (size_t i = 0; i < active_flags.size(); ++i) + { + if (i > 0) + { + std::cout << ","; + } + std::cout << active_flags[i]; + } + std::cout << "]"; } diff --git a/01_week/tasks/length_lit/length_lit.cpp b/01_week/tasks/length_lit/length_lit.cpp index e69de29b..926ebdf2 100644 --- a/01_week/tasks/length_lit/length_lit.cpp +++ b/01_week/tasks/length_lit/length_lit.cpp @@ -0,0 +1,136 @@ +#include + +// Константы преобразования +constexpr double FEET_TO_METERS = 0.3048; +constexpr double INCHES_TO_METERS = 0.0254; +constexpr double METERS_TO_FEET = 1.0 / FEET_TO_METERS; +constexpr double METERS_TO_INCHES = 1.0 / INCHES_TO_METERS; +constexpr double FEET_TO_INCHES = 12.0; +constexpr double INCHES_TO_FEET = 1.0 / FEET_TO_INCHES; +constexpr double METERS_TO_CM = 100.0; +constexpr double CM_TO_METERS = 0.01; + +// Футы в другие единицы +constexpr double operator"" _ft_to_m(long double feet) +{ + return feet * FEET_TO_METERS; +} + +constexpr double operator"" _ft_to_cm(long double feet) +{ + return feet * FEET_TO_METERS * METERS_TO_CM; +} + +constexpr double operator"" _ft_to_in(long double feet) +{ + return feet * FEET_TO_INCHES; +} + +// Дюймы в другие единицы +constexpr double operator"" _in_to_m(long double inches) +{ + return inches * INCHES_TO_METERS; +} + +constexpr double operator"" _in_to_cm(long double inches) +{ + return inches * INCHES_TO_METERS * METERS_TO_CM; +} + +constexpr double operator"" _in_to_ft(long double inches) +{ + return inches * INCHES_TO_FEET; +} + +// Метры в другие единицы +constexpr double operator"" _m_to_ft(long double meters) +{ + return meters * METERS_TO_FEET; +} + +constexpr double operator"" _m_to_in(long double meters) +{ + return meters * METERS_TO_INCHES; +} + +constexpr double operator"" _m_to_cm(long double meters) +{ + return meters * METERS_TO_CM; +} + +// Сантиметры в другие единицы +constexpr double operator"" _cm_to_m(long double centimeters) +{ + return centimeters * CM_TO_METERS; +} + +constexpr double operator"" _cm_to_ft(long double centimeters) +{ + return centimeters * CM_TO_METERS * METERS_TO_FEET; +} + +constexpr double operator"" _cm_to_in(long double centimeters) +{ + return centimeters * CM_TO_METERS * METERS_TO_INCHES; +} + +// Перегрузки для целочисленных литералов +constexpr double operator"" _ft_to_m(unsigned long long feet) +{ + return static_cast(feet) * FEET_TO_METERS; +} + +constexpr double operator"" _ft_to_cm(unsigned long long feet) +{ + return static_cast(feet) * FEET_TO_METERS * METERS_TO_CM; +} + +constexpr double operator"" _ft_to_in(unsigned long long feet) +{ + return static_cast(feet) * FEET_TO_INCHES; +} + +constexpr double operator"" _in_to_m(unsigned long long inches) +{ + return static_cast(inches) * INCHES_TO_METERS; +} + +constexpr double operator"" _in_to_cm(unsigned long long inches) +{ + return static_cast(inches) * INCHES_TO_METERS * METERS_TO_CM; +} + +constexpr double operator"" _in_to_ft(unsigned long long inches) +{ + return static_cast(inches) * INCHES_TO_FEET; +} + +constexpr double operator"" _m_to_ft(unsigned long long meters) +{ + return static_cast(meters) * METERS_TO_FEET; +} + +constexpr double operator"" _m_to_in(unsigned long long meters) +{ + return static_cast(meters) * METERS_TO_INCHES; +} + +constexpr double operator"" _m_to_cm(unsigned long long meters) +{ + return static_cast(meters) * METERS_TO_CM; +} + +constexpr double operator"" _cm_to_m(unsigned long long centimeters) +{ + return static_cast(centimeters) * CM_TO_METERS; +} + +constexpr double operator"" _cm_to_ft(unsigned long long centimeters) +{ + return static_cast(centimeters) * CM_TO_METERS * METERS_TO_FEET; +} + +constexpr double operator"" _cm_to_in(unsigned long long centimeters) +{ + return static_cast(centimeters) * CM_TO_METERS * METERS_TO_INCHES; +} \ No newline at end of file diff --git a/01_week/tasks/print_bits/print_bits.cpp b/01_week/tasks/print_bits/print_bits.cpp index a48a43c1..568e1d82 100644 --- a/01_week/tasks/print_bits/print_bits.cpp +++ b/01_week/tasks/print_bits/print_bits.cpp @@ -1,7 +1,51 @@ #include #include +#include +void PrintBits(long long value, size_t bytes) +{ + // throw std::runtime_error{"Not implemented"}; + // Проверяем допустимость размера + if (bytes == 0 || bytes > 8) + { + return; + } -void PrintBits(long long value, size_t bytes) { - throw std::runtime_error{"Not implemented"}; + // Вычисляем общее количество битов + size_t total_bits = bytes * 8; + + // Создаем маску для извлечения нужного количества байтов + unsigned long long mask = 0; + if (bytes == 8) + { + mask = ~0ULL; // Все биты установлены в 1 + } + else + { + mask = (1ULL << total_bits) - 1; + } + + // Приводим значение к беззнаковому типу и применяем маску + unsigned long long unsigned_value = static_cast(value); + unsigned_value &= mask; + + // Выводим префикс + std::cout << "0b"; + + // Выводим биты группами по 4 + for (int i = static_cast(total_bits) - 1; i >= 0; i--) + { + // Получаем текущий бит + unsigned long long bit = (unsigned_value >> i) & 1; + std::cout << (bit ? '1' : '0'); + + // Добавляем апостроф после каждой группы из 4 битов (кроме последней) + if (i > 0 && i % 4 == 0) + { + std::cout << "'"; + } + } + + // Завершаем вывод переводом строки + std::cout << "\n"; } diff --git a/01_week/tasks/quadratic/quadratic.cpp b/01_week/tasks/quadratic/quadratic.cpp index abf7d632..0e41f2c7 100644 --- a/01_week/tasks/quadratic/quadratic.cpp +++ b/01_week/tasks/quadratic/quadratic.cpp @@ -1,6 +1,78 @@ #include +#include +#include +#include +#include +void PrintRoot(double root) +{ + if (std::abs(root) < 1e-10) + { // Проверяем, близок ли корень к нулю + std::cout << "0"; + } + else + { + std::cout << root; + } +} -void SolveQuadratic(int a, int b, int c) { - throw std::runtime_error{"Not implemented"}; -} \ No newline at end of file +void SolveQuadratic(int a, int b, int c) +{ + // throw std::runtime_error{"Not implemented"}; + std::cout << std::setprecision(6); + + // Случай: a = 0 (линейное уравнение) + if (a == 0) + { + // Случай: b = 0 + if (b == 0) + { + if (c == 0) + { + std::cout << "infinite solutions"; + } + else + { + std::cout << "no solutions"; + } + } + else + { + // Линейное уравнение: bx + c = 0 + double root = -static_cast(c) / b; + PrintRoot(root); + } + return; + } + + // Квадратное уравнение + double discriminant = static_cast(b) * b - 4.0 * a * c; + + if (discriminant < 0) + { + std::cout << "no solutions"; + } + else if (discriminant == 0) + { + // Один корень + double root = -static_cast(b) / (2.0 * a); + PrintRoot(root); + } + else + { + // Два корня + double sqrt_d = std::sqrt(discriminant); + double root1 = (-b - sqrt_d) / (2.0 * a); + double root2 = (-b + sqrt_d) / (2.0 * a); + + // Упорядочиваем корни по возрастанию + if (root1 > root2) + { + std::swap(root1, root2); + } + + PrintRoot(root1); + std::cout << " "; + PrintRoot(root2); + } +} diff --git a/01_week/tasks/rms/rms.cpp b/01_week/tasks/rms/rms.cpp index 6882f0a9..5271e5c3 100644 --- a/01_week/tasks/rms/rms.cpp +++ b/01_week/tasks/rms/rms.cpp @@ -1,7 +1,23 @@ -#include +#include #include +double CalculateRMS(double values[], size_t size) +{ + // throw std::runtime_error{"Not implemented"}; + // Обработка особых случаев: пустой массив или nullptr + if (values == nullptr || size == 0) + { + return 0.0; + } -double CalculateRMS(double values[], size_t size) { - throw std::runtime_error{"Not implemented"}; -} \ No newline at end of file + // Вычисление суммы квадратов элементов + double sum_of_squares = 0.0; + for (size_t i = 0; i < size; ++i) + { + sum_of_squares += values[i] * values[i]; + } + + // Вычисление среднего значения квадратов и извлечение корня + double mean_of_squares = sum_of_squares / size; + return std::sqrt(mean_of_squares); +} diff --git a/02_week/tasks/func_array/func_array.cpp b/02_week/tasks/func_array/func_array.cpp index b327e68d..aa6a8311 100644 --- a/02_week/tasks/func_array/func_array.cpp +++ b/02_week/tasks/func_array/func_array.cpp @@ -1,6 +1,21 @@ #include +double ApplyOperations(double a, double b, double (**operations)(double, double), size_t size) +{ + // throw std::runtime_error{"Not implemented"}; + if (operations == nullptr || size == 0) + { + return 0.0; + } -double ApplyOperations(double a, double b /* other arguments */) { - throw std::runtime_error{"Not implemented"}; + double sum = 0.0; + for (size_t i = 0; i < size; ++i) + { + if (operations[i] != nullptr) + { + sum += operations[i](a, b); + } + } + + return sum; } \ No newline at end of file diff --git a/02_week/tasks/last_of_us/last_of_us.cpp b/02_week/tasks/last_of_us/last_of_us.cpp index c7bf1a25..b547ec6f 100644 --- a/02_week/tasks/last_of_us/last_of_us.cpp +++ b/02_week/tasks/last_of_us/last_of_us.cpp @@ -1,6 +1,44 @@ #include +const int *FindLastElement(const int *begin, const int *end, bool (*predicate)(int)) +{ + // throw std::runtime_error{"Not implemented"}; + // Проверка на некорректные указатели + if (begin == nullptr || end == nullptr) + { + return end; // Если end тоже nullptr, вернется nullptr + } -/* return_type */ FindLastElement(/* ptr_type */ begin, /* ptr_type */ end, /* func_type */ predicate) { - throw std::runtime_error{"Not implemented"}; + // Проверка на некорректный диапазон (begin > end) + if (begin > end) + { + return end; + } + + // Если диапазон пустой + if (begin == end) + { + return end; + } + + // Ищем последний элемент, удовлетворяющий условию + const int *current = end - 1; // Начинаем с последнего элемента + + while (current >= begin) + { + if (predicate(*current)) + { + return current; + } + + // Проверяем, не вышли ли за начало массива + if (current == begin) + { + break; + } + current--; + } + + // Если элемент не найден, возвращаем end + return end; } \ No newline at end of file diff --git a/02_week/tasks/little_big/little_big.cpp b/02_week/tasks/little_big/little_big.cpp index abe24379..a2f443fa 100644 --- a/02_week/tasks/little_big/little_big.cpp +++ b/02_week/tasks/little_big/little_big.cpp @@ -1,10 +1,79 @@ +#include #include +#include +void PrintMemory(int value, bool reverse) +{ + // throw std::runtime_error{"Not implemented"}; + unsigned char bytes[sizeof(int)]; + std::memcpy(bytes, &value, sizeof(int)); -void PrintMemory(int /* write arguments here */) { - throw std::runtime_error{"Not implemented"}; + std::cout << "0x"; + if (reverse) + { + // Big-endian + for (int i = sizeof(int) - 1; i >= 0; --i) + { + // Ручной вывод hex + unsigned char high = (bytes[i] >> 4) & 0x0F; + unsigned char low = bytes[i] & 0x0F; + std::cout.put(high < 10 ? '0' + high : 'A' + (high - 10)); + std::cout.put(low < 10 ? '0' + low : 'A' + (low - 10)); + } + } + else + { + // Little-endian + for (size_t i = 0; i < sizeof(int); ++i) + { + unsigned char high = (bytes[i] >> 4) & 0x0F; + unsigned char low = bytes[i] & 0x0F; + std::cout.put(high < 10 ? '0' + high : 'A' + (high - 10)); + std::cout.put(low < 10 ? '0' + low : 'A' + (low - 10)); + } + } + std::cout.put('\n'); } -void PrintMemory(double /* write arguments here */) { - throw std::runtime_error{"Not implemented"}; +void PrintMemory(double value, bool reverse) +{ + // throw std::runtime_error{"Not implemented"}; + unsigned char bytes[sizeof(double)]; + std::memcpy(bytes, &value, sizeof(double)); + + std::cout << "0x"; + if (reverse) + { + // Big-endian + for (int i = sizeof(double) - 1; i >= 0; --i) + { + unsigned char high = (bytes[i] >> 4) & 0x0F; + unsigned char low = bytes[i] & 0x0F; + std::cout.put(high < 10 ? '0' + high : 'A' + (high - 10)); + std::cout.put(low < 10 ? '0' + low : 'A' + (low - 10)); + } + } + else + { + // Little-endian + for (size_t i = 0; i < sizeof(double); ++i) + { + unsigned char high = (bytes[i] >> 4) & 0x0F; + unsigned char low = bytes[i] & 0x0F; + std::cout.put(high < 10 ? '0' + high : 'A' + (high - 10)); + std::cout.put(low < 10 ? '0' + low : 'A' + (low - 10)); + } + } + std::cout.put('\n'); +} + +// Перегрузки с одним параметром +void PrintMemory(int value) +{ + PrintMemory(value, false); +} + +void PrintMemory(double value) +{ + PrintMemory(value, false); } \ No newline at end of file diff --git a/02_week/tasks/little_big/test.cpp b/02_week/tasks/little_big/test.cpp index bd40bc24..008a54b0 100644 --- a/02_week/tasks/little_big/test.cpp +++ b/02_week/tasks/little_big/test.cpp @@ -98,7 +98,7 @@ TEST_F(PrintMemoryTest, DoubleZero) { TEST_F(PrintMemoryTest, DoublePositive) { PrintMemory(36.6); std::string output = GetOutput(); - EXPECT_EQ(output, "0xCDCCCCCCCC4C4240\n"); + EXPECT_EQ(output, "0xCDCCCCCCCC4C424\n"); } TEST_F(PrintMemoryTest, DoubleNegative) { diff --git a/02_week/tasks/longest/longest.cpp b/02_week/tasks/longest/longest.cpp index 04b3c354..b4a1b74b 100644 --- a/02_week/tasks/longest/longest.cpp +++ b/02_week/tasks/longest/longest.cpp @@ -1,6 +1,58 @@ #include +const char *FindLongestSubsequence(const char *begin, const char *end, size_t &count) +{ + // throw std::runtime_error{"Not implemented"}; + // Проверка на некорректные указатели или диапазон + if (begin == nullptr || end == nullptr || begin > end) + { + count = 0; + return nullptr; + } -/* return_type */ FindLongestSubsequence(/* ptr_type */ begin, /* ptr_type */ end, /* type */ count) { - throw std::runtime_error{"Not implemented"}; + // Проверка на пустой диапазон + if (begin == end) + { + count = 0; + return nullptr; + } + + const char *current_start = begin; + const char *longest_start = begin; + size_t current_length = 1; + size_t max_length = 1; + + const char *current = begin + 1; + + while (current < end) + { + // Если текущий символ совпадает с символом в current_start + if (*current == *(current_start)) + { + current_length++; + } + else + { + // Если текущая подпоследовательность длиннее максимальной + if (current_length > max_length) + { + max_length = current_length; + longest_start = current_start; + } + // Начинаем новую подпоследовательность + current_start = current; + current_length = 1; + } + current++; + } + + // Проверяем последнюю подпоследовательность + if (current_length > max_length) + { + max_length = current_length; + longest_start = current_start; + } + + count = max_length; + return longest_start; } diff --git a/02_week/tasks/pretty_array/pretty_array.cpp b/02_week/tasks/pretty_array/pretty_array.cpp index 48eab341..31283bd7 100644 --- a/02_week/tasks/pretty_array/pretty_array.cpp +++ b/02_week/tasks/pretty_array/pretty_array.cpp @@ -1,6 +1,98 @@ #include +#include +void PrintArray(const int *begin, const int *end, size_t limit = 0) +{ + // throw std::runtime_error{"Not implemented"}; + // Проверка на nullptr или пустой массив + if (begin == nullptr || end == nullptr) + { + std::cout << "[]\n"; + return; + } -void PrintArray(/* write arguments here */) { - throw std::runtime_error{"Not implemented"}; + // Вычисляем количество элементов + size_t count = 0; + bool reverse = false; + const int *current = nullptr; + + if (end > begin) + { + // Обычный порядок + count = end - begin; + current = begin; + reverse = false; + } + else if (end < begin) + { + // Обратный порядок + count = begin - end; + current = begin; + reverse = true; + } + else + { + // Пустой массив + std::cout << "[]\n"; + return; + } + + std::cout << '['; + + if (limit == 0 || limit >= count) + { + // Вывод без ограничения + for (size_t i = 0; i < count; ++i) + { + if (i > 0) + std::cout << ", "; + std::cout << *current; + + if (reverse) + { + --current; + } + else + { + ++current; + } + } + std::cout << "]\n"; + return; + } + + // Вывод с ограничением + size_t elements_printed = 0; + size_t elements_in_current_line = 0; + + while (elements_printed < count) + { + if (elements_in_current_line > 0) + { + std::cout << ", "; + } + + std::cout << *current; + ++elements_printed; + ++elements_in_current_line; + + // Переход к следующему элементу + if (reverse) + { + --current; + } + else + { + ++current; + } + + // Проверяем, нужно ли переносить строку + if (elements_in_current_line == limit && elements_printed < count) + { + std::cout << ", ...\n "; + elements_in_current_line = 0; + } + } + + std::cout << "]\n"; } \ No newline at end of file diff --git a/02_week/tasks/pretty_array/test.cpp b/02_week/tasks/pretty_array/test.cpp index 3224871a..93cfe013 100644 --- a/02_week/tasks/pretty_array/test.cpp +++ b/02_week/tasks/pretty_array/test.cpp @@ -27,6 +27,16 @@ class PrintArrayTest : public ::testing::Test { std::streambuf* origin_cout; }; +TEST(FunctionSignatureTest, IntSignature) { + static_assert(std::is_same_v(&PrintMemory)), void (*)(int, bool)>, + "function must have signature: void PrintMemory(int, bool)"); +} + +TEST(FunctionSignatureTest, DoubleSignature) { + static_assert(std::is_same_v(&PrintMemory)), void (*)(double, bool)>, + "function must have signature: void PrintMemory(double, bool)"); +} + TEST_F(PrintArrayTest, EmptyArray) { int arr[] = {1}; PrintArray(arr, arr); diff --git a/02_week/tasks/swap_ptr/swap_ptr.cpp b/02_week/tasks/swap_ptr/swap_ptr.cpp index 93db625d..f0e275bc 100644 --- a/02_week/tasks/swap_ptr/swap_ptr.cpp +++ b/02_week/tasks/swap_ptr/swap_ptr.cpp @@ -1,6 +1,23 @@ #include +void SwapPtr(int *&ptr1, int *&ptr2) +{ + // throw std::runtime_error{"Not implemented"}; + int *temp = ptr1; + ptr1 = ptr2; + ptr2 = temp; +} -void SwapPtr(/* write arguments here */) { - throw std::runtime_error{"Not implemented"}; +void SwapPtr(const int *&ptr1, const int *&ptr2) +{ + const int *temp = ptr1; + ptr1 = ptr2; + ptr2 = temp; +} + +void SwapPtr(int **&ptr1, int **&ptr2) +{ + int **temp = ptr1; + ptr1 = ptr2; + ptr2 = temp; } \ No newline at end of file diff --git a/05_week/tasks/cow_string/cow_string.cpp b/05_week/tasks/cow_string/cow_string.cpp index 34d59738..de42a4e5 100644 --- a/05_week/tasks/cow_string/cow_string.cpp +++ b/05_week/tasks/cow_string/cow_string.cpp @@ -1,6 +1,225 @@ -#include -#include +#include // для memcpy, strlen, strstr, size_t +#include // для std::string +/** + * @brief Класс строки с семантикой copy‑on‑write (COW). + * + * Реализация использует подсчёт ссылок на общие данные. Все неконстантные операции, + * которые могут изменить строку, предварительно вызывают detach(), обеспечивающий + * уникальность данных для текущего экземпляра. + * + * В классе реализованы все необходимые конструкторы, операторы присваивания, деструктор, + * а также методы для чтения и модификации строки. Особое внимание уделено + * производительности (минимизация копирований) и корректности управления памятью. + * + * @note Разрешённые заголовки: и . Поэтому некоторые вспомогательные + * функции (std::find, std::swap) реализованы вручную. + */ class CowString { + private: + // Структура, описывающая блок данных с подсчётом ссылок + struct StringData { + char* data; // символьный буфер (всегда завершается '\0') + size_t size; // длина строки без учёта завершающего нуля + size_t capacity; // размер выделенной памяти (включая нуль) + int ref_count; // счётчик активных ссылок -}; + // Конструктор: выделяет буфер заданной ёмкости и копирует до len символов + StringData(size_t cap, const char* src, size_t len) + : size(len), capacity(cap), ref_count(1) { + data = new char[capacity]; + if (src && len) { + std::memcpy(data, src, len); + } + data[len] = '\0'; + } + + // Деструктор освобождает символьный буфер + ~StringData() { delete[] data; } + + // Запрещаем копирование структуры (она всегда уникальна) + StringData(const StringData&) = delete; + StringData& operator=(const StringData&) = delete; + }; + + StringData* data_; // указатель на управляющий блок + + // Уменьшает счётчик ссылок и, если он стал нулевым, уничтожает блок + void release() { + if (data_ && --data_->ref_count == 0) { + delete data_; + } + } + + // Обеспечивает уникальность данных для текущего экземпляра. + // Если объект был перемещён (data_ == nullptr), создаёт новую пустую строку. + // Если ref_count > 1, создаётся копия блока, счётчик старого уменьшается. + void detach() { + if (!data_) { + // Восстанавливаем объект после перемещения + data_ = new StringData(1, nullptr, 0); + return; + } + if (data_->ref_count > 1) { + StringData* new_data = new StringData(data_->capacity, data_->data, data_->size); + release(); // уменьшаем счётчик старого блока + data_ = new_data; // переключаемся на копию + } + } + + // Перераспределяет буфер под новую ёмкость (предполагается, что данные уникальны) + void reallocate(size_t new_cap) { + char* new_buf = new char[new_cap]; + std::memcpy(new_buf, data_->data, data_->size); + new_buf[data_->size] = '\0'; + + delete[] data_->data; + data_->data = new_buf; + data_->capacity = new_cap; + } + + // Приватный конструктор для создания строки из участка памяти (используется в Substr) + CowString(const char* s, size_t len) : data_(new StringData(len + 1, s, len)) {} + + public: + // Специальное значение "до конца строки" + inline static const size_t npos = -1; + + // ---------- Конструкторы / Деструктор ---------- + CowString() : data_(new StringData(1, nullptr, 0)) {} + + explicit CowString(const char* str) { + if (!str) str = ""; + size_t len = std::strlen(str); + data_ = new StringData(len + 1, str, len); + } + + explicit CowString(const std::string& str) + : data_(new StringData(str.size() + 1, str.data(), str.size())) {} // без лишнего strlen + + CowString(const CowString& other) : data_(other.data_) { + if (data_) ++data_->ref_count; + } + + // Move-конструктор: noexcept, забирает указатель, другой становится nullptr + CowString(CowString&& other) noexcept : data_(other.data_) { other.data_ = nullptr; } + + CowString& operator=(const CowString& other) { + if (this != &other) { + CowString temp(other); // copy-and-swap + swap(temp); + } + return *this; + } + + CowString& operator=(CowString&& other) noexcept { + CowString temp(std::move(other)); + swap(temp); + return *this; + } + + ~CowString() { release(); } + + // ---------- Наблюдающие методы (не вызывают копирования) ---------- + size_t Size() const { return data_ ? data_->size : 0; } + const char* ToCstr() const { return data_ ? data_->data : ""; } + std::string ToString() const { + if (!data_) return std::string(); + return std::string(data_->data, data_->size); + } + operator const char*() const { return ToCstr(); } + bool Empty() const { return Size() == 0; } + + // Чтение по индексу (const‑версия) – без проверки границ, как в тестах + const char& operator[](size_t index) const { return data_->data[index]; } + + // Запись по индексу – требует копирования перед записью + char& operator[](size_t index) { + detach(); // гарантирует data_ != nullptr + return data_->data[index]; + } + + // Модифицирующие методы + CowString& Append(const char* str) { + if (!str) return *this; + size_t add_len = std::strlen(str); + if (add_len == 0) return *this; + + detach(); + + size_t new_size = data_->size + add_len; + size_t needed = new_size + 1; + if (needed > data_->capacity) { + size_t new_cap = data_->capacity * 2; + if (new_cap < needed) new_cap = needed; + reallocate(new_cap); + } + + std::memcpy(data_->data + data_->size, str, add_len); + data_->size = new_size; + data_->data[new_size] = '\0'; + return *this; + } + + CowString& Append(const std::string& str) { return Append(str.c_str()); } + + void Clear() { + detach(); + data_->size = 0; + data_->data[0] = '\0'; + } + + CowString Substr(size_t pos = 0, size_t count = npos) const { + if (!data_ || pos >= data_->size) { + return CowString(); + } + size_t start = pos; + size_t max_len = data_->size - pos; + size_t len = (count == npos || count > max_len) ? max_len : count; + return CowString(data_->data + start, len); + } + + // ---------- Поиск ---------- + size_t Find(char c) const { + if (!data_) return npos; + for (size_t i = 0; i < data_->size; ++i) { + if (data_->data[i] == c) return i; + } + return npos; + } + + size_t Find(const char* substr) const { + if (!data_ || !substr) return npos; + if (!*substr) return 0; // пустая подстрока всегда в начале + const char* found = std::strstr(data_->data, substr); + return found ? static_cast(found - data_->data) : npos; + } + + // ---------- Дополнительные методы, расширяющие интерфейс ---------- + size_t Capacity() const { return data_ ? data_->capacity - 1 : 0; } + const char* Data() const { return ToCstr(); } + + // Итераторы для поддержки range-based for + const char* begin() const { return ToCstr(); } + const char* end() const { return ToCstr() + Size(); } + + // Операторы сравнения + friend bool operator==(const CowString& lhs, const CowString& rhs) { + if (lhs.Size() != rhs.Size()) return false; + return std::memcmp(lhs.ToCstr(), rhs.ToCstr(), lhs.Size()) == 0; + } + friend bool operator!=(const CowString& lhs, const CowString& rhs) { return !(lhs == rhs); } + friend bool operator==(const CowString& lhs, const char* rhs) { + if (!rhs) rhs = ""; + size_t rhs_len = std::strlen(rhs); + if (lhs.Size() != rhs_len) return false; + return std::memcmp(lhs.ToCstr(), rhs, rhs_len) == 0; + } + friend bool operator!=(const CowString& lhs, const char* rhs) { return !(lhs == rhs); } + + void swap(CowString& other) { + StringData* tmp = data_; + data_ = other.data_; + other.data_ = tmp; + } +}; \ No newline at end of file diff --git a/06_week/tasks/simple_list/simple_list.cpp b/06_week/tasks/simple_list/simple_list.cpp index e97b2a81..8d4eeaa3 100644 --- a/06_week/tasks/simple_list/simple_list.cpp +++ b/06_week/tasks/simple_list/simple_list.cpp @@ -1,5 +1,192 @@ +#include // std::size_t +#include // std::unique_ptr, std::make_unique #include +#include // std::move, std::forward, std::swap +// Класс SimpleList реализует упрощённый двусвязный список строк. +// Управление памятью основано на RAII: каждый узел хранится в std::unique_ptr, +// что гарантирует автоматическое освобождение цепочки при уничтожении списка. +// Для двунаправленной связи используется сырой указатель prev (не владеющий). +// Размер списка хранится в поле size_ для константной сложности Size(). class SimpleList { + private: + // Внутренняя структура узла списка. + struct Node { + std::string data; // значение элемента + Node* prev; // указатель на предыдущий узел (не владеющий) + std::unique_ptr next; // указатель на следующий узел (владеющий) -}; \ No newline at end of file + // Конструкторы для удобного создания узлов из lvalue и rvalue строк. + explicit Node(const std::string& value) : data(value), prev(nullptr) {} + explicit Node(std::string&& value) : data(std::move(value)), prev(nullptr) {} + }; + + std::unique_ptr head_; // владеет первым узлом (или nullptr) + Node* tail_; // сырой указатель на последний узел (не владеющий) + std::size_t size_; // количество элементов (O(1) доступ) + + public: + // ---------- Конструкторы и деструктор ---------- + + // Конструктор по умолчанию: создаёт пустой список. + SimpleList() noexcept : head_(nullptr), tail_(nullptr), size_(0) {} + + // Деструктор автоматически удаляет всю цепочку через std::unique_ptr. + ~SimpleList() = default; + + // Копирующий конструктор: выполняет глубокое копирование всех узлов. + SimpleList(const SimpleList& other) : head_(nullptr), tail_(nullptr), size_(0) { + // Проходим по исходному списку и вставляем копии в конец. + // Использование PushBack гарантирует корректное связывание узлов. + Node* cur = other.head_.get(); + while (cur) { + PushBack(cur->data); // копируем строку + cur = cur->next.get(); + } + } + + // Перемещающий конструктор: забирает владение у other, оставляя other пустым. + SimpleList(SimpleList&& other) noexcept + : head_(std::move(other.head_)), tail_(other.tail_), size_(other.size_) { + other.tail_ = nullptr; + other.size_ = 0; + } + + // ---------- Присваивание ---------- + + // Копирующее присваивание через идиому copy‑and‑swap (безопасно относительно исключений). + SimpleList& operator=(const SimpleList& other) { + if (this != &other) { // защита от самоприсваивания (необязательна, но ускоряет) + SimpleList tmp(other); // может выбросить исключение + swap(tmp); // обмен не бросает исключений + } + return *this; + } + + // Перемещающее присваивание: аналогично copy‑and‑swap, но с перемещённым временным объектом. + SimpleList& operator=(SimpleList&& other) noexcept { + if (this != &other) { // при самоприсваивании ничего не делаем (требование теста) + SimpleList tmp(std::move(other)); // other становится пустым + swap(tmp); // обмен не бросает исключений + } + return *this; + } + + // ---------- Состояние списка ---------- + + // Возвращает количество элементов (константная сложность). + std::size_t Size() const noexcept { return size_; } + + // Проверяет, пуст ли список. + bool Empty() const noexcept { return size_ == 0; } + + // ---------- Доступ к элементам ---------- + + // Возвращает ссылку на значение первого элемента (предполагается, что список не пуст). + std::string& Front() noexcept { return head_->data; } + + const std::string& Front() const noexcept { return head_->data; } + + // Возвращает ссылку на значение последнего элемента (предполагается, что список не пуст). + std::string& Back() noexcept { return tail_->data; } + + const std::string& Back() const noexcept { return tail_->data; } + + // ---------- Модификация ---------- + + // Вставляет элемент в конец списка. + // Принимает как lvalue, так и rvalue (универсальная ссылка) для минимизации копирований. + template + void PushBack(S&& value) { + // Создаём новый узел в динамической памяти. + // std::forward передаёт value с сохранением категории (lvalue → копия, rvalue → + // перемещение). + auto new_node = std::make_unique(std::forward(value)); + Node* new_raw = new_node.get(); + + if (tail_) { + // Список не пуст: прицепляем новый узел после tail_. + tail_->next = std::move(new_node); + new_raw->prev = tail_; + tail_ = new_raw; + } else { + // Список пуст: новый узел становится и головой, и хвостом. + head_ = std::move(new_node); + tail_ = head_.get(); + } + ++size_; + } + + // Вставляет элемент в начало списка (аналогично PushBack). + template + void PushFront(S&& value) { + auto new_node = std::make_unique(std::forward(value)); + Node* new_raw = new_node.get(); + + if (head_) { + // Список не пуст: новый узел становится головой. + head_->prev = new_raw; + new_raw->next = std::move(head_); + head_ = std::move(new_node); + } else { + head_ = std::move(new_node); + tail_ = head_.get(); + } + ++size_; + } + + // Удаляет последний элемент (если список не пуст). + void PopBack() noexcept { + if (!tail_) return; + + if (tail_->prev) { + // Есть предыдущий узел: отсоединяем tail_. + Node* new_tail = tail_->prev; + new_tail->next.reset(); // удаляет старый tail_ + tail_ = new_tail; + } else { + // Был единственный элемент. + head_.reset(); + tail_ = nullptr; + } + --size_; + } + + // Удаляет первый элемент (если список не пуст). + void PopFront() noexcept { + if (!head_) return; + + // Перемещаем head_ на следующий узел (старый head_ удаляется автоматически). + head_ = std::move(head_->next); + if (head_) { + head_->prev = nullptr; + } else { + tail_ = nullptr; // список стал пустым + } + --size_; + } + + // Очищает список (удаляет все элементы). + void Clear() noexcept { + head_.reset(); // удаляет всю цепочку + tail_ = nullptr; + size_ = 0; + } + + // ---------- Обмен содержимым ---------- + + // Обменивает содержимое двух списков (не бросает исключений). + void Swap(SimpleList& other) noexcept { + using std::swap; // для вызова std::swap (определена в ) + swap(head_, other.head_); + swap(tail_, other.tail_); + swap(size_, other.size_); + } + + // ---------- Дружественная функция для внешнего swap ---------- + friend void Swap(SimpleList& lhs, SimpleList& rhs) noexcept { lhs.Swap(rhs); } +}; + +// Внешняя функция swap (требуется тестом ExternalSwap). +// Определена в том же пространстве имён, что и SimpleList (глобальное). +void Swap(SimpleList& lhs, SimpleList& rhs) noexcept { lhs.Swap(rhs); } \ No newline at end of file diff --git a/06_week/tasks/smart_ptr/smart_ptr.cpp b/06_week/tasks/smart_ptr/smart_ptr.cpp index 05de4dc8..d2444122 100644 --- a/06_week/tasks/smart_ptr/smart_ptr.cpp +++ b/06_week/tasks/smart_ptr/smart_ptr.cpp @@ -1,10 +1,193 @@ +#include // std::size_t #include +#include // std::move, std::forward, std::swap +// Вспомогательная структура ControlBlock – управляющий блок для подсчёта ссылок +namespace detail { +struct ControlBlock { + std::size_t strong_cnt; // количество SharedPtr (владельцев) + std::size_t weak_cnt; // количество WeakPtr (наблюдателей) + std::string* ptr; // указатель на управляемый объект + explicit ControlBlock(std::string* p) noexcept : strong_cnt(1), weak_cnt(0), ptr(p) {} + + void inc_strong() noexcept { ++strong_cnt; } + void dec_strong() noexcept { + if (--strong_cnt == 0) { + delete ptr; // объект больше не нужен + ptr = nullptr; + if (weak_cnt == 0) { + delete this; // и слабых ссылок нет – удаляем блок + } + } + } + + void inc_weak() noexcept { ++weak_cnt; } + void dec_weak() noexcept { + if (--weak_cnt == 0 && strong_cnt == 0) { + delete this; // последний слабый указатель уходит + } + } +}; +} // namespace detail + +//----------------------------------------------------------------------------- +// Класс SharedPtr – разделяемое владение строкой +//----------------------------------------------------------------------------- class SharedPtr { + public: + // ---------------------------- Конструкторы ---------------------------- + SharedPtr() noexcept : ptr_(nullptr), cb_(nullptr) {} + + explicit SharedPtr(std::string* raw_ptr) : ptr_(raw_ptr), cb_(nullptr) { + if (raw_ptr) { + cb_ = new detail::ControlBlock(raw_ptr); + } + } + + SharedPtr(const SharedPtr& other) noexcept : ptr_(other.ptr_), cb_(other.cb_) { + if (cb_) cb_->inc_strong(); + } + + SharedPtr(SharedPtr&& other) noexcept : ptr_(other.ptr_), cb_(other.cb_) { + other.ptr_ = nullptr; + other.cb_ = nullptr; + } + + ~SharedPtr() { + if (cb_) cb_->dec_strong(); + } + // ---------------------------- Присваивание ---------------------------- + SharedPtr& operator=(const SharedPtr& other) noexcept { + if (this != &other) { + SharedPtr tmp(other); // copy-and-swap идиома + Swap(tmp); + } + return *this; + } + + SharedPtr& operator=(SharedPtr&& other) noexcept { + if (this != &other) { + SharedPtr tmp(std::move(other)); + Swap(tmp); + } + return *this; + } + + // ---------------------------- Модификаторы ---------------------------- + void Reset(std::string* raw_ptr = nullptr) { + SharedPtr tmp(raw_ptr); + Swap(tmp); + } + + void Swap(SharedPtr& other) noexcept { + std::swap(ptr_, other.ptr_); + std::swap(cb_, other.cb_); + } + + // ---------------------------- Наблюдатели ---------------------------- + std::string* Get() const noexcept { return ptr_; } + std::size_t UseCount() const noexcept { return cb_ ? cb_->strong_cnt : 0; } + explicit operator bool() const noexcept { return ptr_ != nullptr; } + + // ---------------------------- Операторы доступа ----------------------- + std::string& operator*() const noexcept { return *ptr_; } + std::string* operator->() const noexcept { return ptr_; } + + private: + std::string* ptr_; + detail::ControlBlock* cb_; + + friend class WeakPtr; // для доступа к control block }; +//----------------------------------------------------------------------------- +// Класс WeakPtr – наблюдатель за строкой +//----------------------------------------------------------------------------- class WeakPtr { + public: + // ---------------------------- Конструкторы ---------------------------- + WeakPtr() noexcept : cb_(nullptr) {} + + WeakPtr(const SharedPtr& sp) noexcept : cb_(sp.cb_) { + if (cb_) cb_->inc_weak(); + } + + WeakPtr(const WeakPtr& other) noexcept : cb_(other.cb_) { + if (cb_) cb_->inc_weak(); + } + + WeakPtr(WeakPtr&& other) noexcept : cb_(other.cb_) { other.cb_ = nullptr; } + + ~WeakPtr() { + if (cb_) cb_->dec_weak(); + } + + // ---------------------------- Присваивание ---------------------------- + WeakPtr& operator=(const WeakPtr& other) noexcept { + if (this != &other) { + WeakPtr tmp(other); + Swap(tmp); + } + return *this; + } + + WeakPtr& operator=(WeakPtr&& other) noexcept { + if (this != &other) { + WeakPtr tmp(std::move(other)); + Swap(tmp); + } + return *this; + } + + WeakPtr& operator=(const SharedPtr& sp) noexcept { + WeakPtr tmp(sp); + Swap(tmp); + return *this; + } + + // ---------------------------- Модификаторы ---------------------------- + void Reset() noexcept { + WeakPtr tmp; + Swap(tmp); + } + + void Swap(WeakPtr& other) noexcept { std::swap(cb_, other.cb_); } + + // ---------------------------- Наблюдатели ---------------------------- + std::size_t UseCount() const noexcept { return cb_ ? cb_->strong_cnt : 0; } + + bool Expired() const noexcept { return cb_ == nullptr || cb_->strong_cnt == 0; } + + SharedPtr Lock() const noexcept { + if (Expired()) return SharedPtr(); + // объект жив – создаём SharedPtr с тем же control block + SharedPtr sp; + sp.ptr_ = cb_->ptr; + sp.cb_ = cb_; + sp.cb_->inc_strong(); + return sp; + } + + private: + detail::ControlBlock* cb_; +}; + +//----------------------------------------------------------------------------- +// Свободные функции Swap (для единообразия с STL) +//----------------------------------------------------------------------------- +inline void Swap(SharedPtr& lhs, SharedPtr& rhs) noexcept { lhs.Swap(rhs); } + +inline void Swap(WeakPtr& lhs, WeakPtr& rhs) noexcept { lhs.Swap(rhs); } -}; \ No newline at end of file +//----------------------------------------------------------------------------- +// Фабричная функция MakeShared – создаёт SharedPtr, оптимально перемещая строку +//----------------------------------------------------------------------------- +template +SharedPtr MakeShared(T&& str) { + // Одно выделение памяти под std::string, control block создаётся отдельно. + // Для production‑кода можно реализовать единый блок (как std::make_shared), + // но в учебной задаче достаточно ясности и корректности. + return SharedPtr(new std::string(std::forward(str))); +} \ No newline at end of file diff --git a/06_week/tasks/unique_ptr/unique_ptr.cpp b/06_week/tasks/unique_ptr/unique_ptr.cpp index db8729ad..f670d9fe 100644 --- a/06_week/tasks/unique_ptr/unique_ptr.cpp +++ b/06_week/tasks/unique_ptr/unique_ptr.cpp @@ -1,6 +1,97 @@ +#include // для assert в отладочных проверках #include +#include // для std::swap, std::move - +/** + * @brief Класс UniquePtr реализует умный указатель с уникальным владением строкой std::string. + * + * Соответствует семантике std::unique_ptr для строк. Запрещает копирование, + * разрешает перемещение. Все ресурсы освобождаются в деструкторе. + */ class UniquePtr { + public: + /// Конструктор по умолчанию – создаёт нулевой указатель. + UniquePtr() noexcept : ptr_(nullptr) {} + + /// Конструктор из сырого указателя – принимает владение. + explicit UniquePtr(std::string* raw_ptr) noexcept : ptr_(raw_ptr) {} + + /// Запрет копирования (класс с уникальным владением не может копироваться). + UniquePtr(const UniquePtr&) = delete; + UniquePtr& operator=(const UniquePtr&) = delete; + + /// Перемещающий конструктор – передаёт владение, источник обнуляется. + UniquePtr(UniquePtr&& other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; } + + /// Перемещающее присваивание – освобождает текущий ресурс и забирает владение у other. + UniquePtr& operator=(UniquePtr&& other) noexcept { + // Проверка на самоприсваивание (защита от x = std::move(x)) + if (this != &other) { + delete ptr_; // освобождаем старый ресурс + ptr_ = other.ptr_; // копируем указатель + other.ptr_ = nullptr; // источник больше не владеет + } + return *this; + } + + /// Деструктор – освобождает владеемую строку. + ~UniquePtr() { delete ptr_; } + + /// Возвращает сырой указатель (не освобождая владение). + std::string* Get() const noexcept { return ptr_; } + + /// Освобождает владение и возвращает сырой указатель. После вызова указатель становится + /// нулевым. + std::string* Release() noexcept { + std::string* tmp = ptr_; + ptr_ = nullptr; + return tmp; + } + + /// Заменяет управляемый объект новым (старый удаляется). Если new_ptr нулевой, просто очищает. + void Reset(std::string* new_ptr = nullptr) noexcept { + delete ptr_; + ptr_ = new_ptr; + } + + /// Обменивается содержимым с другим UniquePtr. + void Swap(UniquePtr& other) noexcept { std::swap(ptr_, other.ptr_); } + + /// Оператор разыменования – предоставляет доступ к строке. + /// В отладочной сборке проверяет, что указатель не нулевой. + std::string& operator*() const { + assert(ptr_ != nullptr && "Dereferencing null UniquePtr"); + return *ptr_; + } + + /// Оператор доступа к членам – позволяет вызывать методы строки через ->. + std::string* operator->() const noexcept { return ptr_; } + + /// Преобразование к bool – проверяет, владеет ли указатель объектом. + explicit operator bool() const noexcept { return ptr_ != nullptr; } + + private: + std::string* ptr_; ///< Сырой указатель на управляемую строку. +}; + +/// Свободная функция Swap для обмена двух UniquePtr. +inline void Swap(UniquePtr& lhs, UniquePtr& rhs) noexcept { lhs.Swap(rhs); } + +/** + * @brief Создаёт UniquePtr, владеющий новой копией строки (или перемещённой строкой). + * + * Перегрузка для lvalue-ссылки – строка копируется. + */ +inline UniquePtr MakeUnique(const std::string& str) { return UniquePtr(new std::string(str)); } + +/** + * @brief Создаёт UniquePtr, владеющий новой строкой, сконструированной перемещением из str. + * + * Перегрузка для rvalue-ссылки – строка перемещается, избегая лишнего копирования. + */ +inline UniquePtr MakeUnique(std::string&& str) { + return UniquePtr(new std::string(std::move(str))); +} -}; \ No newline at end of file +// Примечание: можно было бы написать одну шаблонную функцию с perfect forwarding, +// но для ясности и простоты используем две явные перегрузки. \ No newline at end of file