diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..261348ae --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +build +build-asan +.vscode +main +main.cpp \ No newline at end of file diff --git a/01_week/tasks/addition/addition.cpp b/01_week/tasks/addition/addition.cpp index 92872802..b9263084 100644 --- a/01_week/tasks/addition/addition.cpp +++ b/01_week/tasks/addition/addition.cpp @@ -1,7 +1,6 @@ #include -#include 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..9d1bce8b 100644 --- a/01_week/tasks/char_changer/char_changer.cpp +++ b/01_week/tasks/char_changer/char_changer.cpp @@ -1,7 +1,53 @@ #include -#include +#include size_t CharChanger(char array[], size_t size, char delimiter = ' ') { - throw std::runtime_error{"Not implemented"}; + // pos - позиция в массиве array без дубликатов + size_t pos = 0; + + // size - размер массива с учётом '\0' в конце + for (size_t i = 0; i < size - 1; ++i) { + // сохраняем символ до его замены и приводим его к uint + // функции isdigit, islower и др. не работают с отрицательным кодами + char old_symbol = static_cast(array[i]); + + // определяем тип замены + if (isdigit(old_symbol)) { + array[pos++] = '*'; + } + else if (old_symbol == ' ') { + array[pos++] = delimiter; + } + else if (islower(old_symbol)) { + array[pos++] = toupper(old_symbol); + } + else if (isupper(old_symbol)) { + array[pos++] = old_symbol; + } + else { + array[pos++] = '_'; + } + + // поиск и подсчёт дубликатов + size_t j = i + 1; + while (j < size && array[j] == old_symbol) { + ++j; + } + + // если есть дубликаты + if (size_t duplicates = j - i - 1; duplicates > 0) { + // обновляем указатель i + i += duplicates; + + // добавляем счётчик после повторяющегося символа (кроме ' ') + if (old_symbol != ' ') + array[pos++] = duplicates + 1 >= 10 ? '0' : duplicates + 1 + '0'; + } + } + + // устанавливаем новый конец массива + array[pos] = '\0'; + + return pos; } diff --git a/01_week/tasks/check_flags/check_flags.cpp b/01_week/tasks/check_flags/check_flags.cpp index 75e7c652..4baf93e1 100644 --- a/01_week/tasks/check_flags/check_flags.cpp +++ b/01_week/tasks/check_flags/check_flags.cpp @@ -1,6 +1,7 @@ #include -#include - +#include +#include +#include enum class CheckFlags : uint8_t { NONE = 0, @@ -13,6 +14,40 @@ enum class CheckFlags : uint8_t { ALL = TIME | DATE | USER | CERT | KEYS | DEST }; +CheckFlags operator&(CheckFlags lhs, CheckFlags rhs) { + return static_cast(static_cast(lhs) & static_cast(rhs)); +} + void PrintCheckFlags(CheckFlags flags) { - throw std::runtime_error{"Not implemented"}; + // Проверка на выход за диапозон значений CheckFlags + if ((~static_cast(CheckFlags::ALL) & static_cast(flags)) != 0) { + return; + } + + const static std::pair checks[] { + {CheckFlags::TIME, "TIME"}, + {CheckFlags::DATE, "DATE"}, + {CheckFlags::USER, "USER"}, + {CheckFlags::CERT, "CERT"}, + {CheckFlags::KEYS, "KEYS"}, + {CheckFlags::DEST, "DEST"} + }; + + bool first = true; + + std::cout << "["; + + for (const auto& [flag, name] : checks) { + if ((flags & flag) != CheckFlags::NONE) { + if (first) { + first = false; + } + else { + std::cout << ","; + } + std::cout << name; + } + } + + std::cout << ']'; } diff --git a/01_week/tasks/length_lit/length_lit.cpp b/01_week/tasks/length_lit/length_lit.cpp index e69de29b..e5f29adb 100644 --- a/01_week/tasks/length_lit/length_lit.cpp +++ b/01_week/tasks/length_lit/length_lit.cpp @@ -0,0 +1,60 @@ +namespace { + constexpr long double INCHES_PER_FOOT = 12.0L; + constexpr long double CM_PER_INCH = 2.54L; + constexpr long double M_PER_FOOT = 0.3048L; + constexpr long double CM_PER_FOOT = M_PER_FOOT * 100.0L; + constexpr long double M_PER_INCH = CM_PER_INCH / 100.0L; + constexpr long double CM_PER_METER = 100.0L; +} + +// ==== Перевод футов ==== +constexpr long double operator""_ft_to_m(long double ft) { + return ft * M_PER_FOOT; +} + +constexpr long double operator""_ft_to_cm(long double ft) { + return ft * CM_PER_FOOT; +} + +constexpr long double operator""_ft_to_in(long double ft) { + return ft * INCHES_PER_FOOT; +} + +// ==== Перевод дюймов ==== +constexpr long double operator""_in_to_m(long double in) { + return in * M_PER_INCH; +} + +constexpr long double operator""_in_to_cm(long double in) { + return in * CM_PER_INCH; +} + +constexpr long double operator""_in_to_ft(long double in) { + return in / INCHES_PER_FOOT; +} + +// ==== Перевод метров ==== +constexpr long double operator""_m_to_ft(long double m) { + return m / M_PER_FOOT; +} + +constexpr long double operator""_m_to_in(long double m) { + return m / M_PER_INCH; +} + +constexpr long double operator""_m_to_cm(long double m) { + return m * CM_PER_METER; +} + +// ==== Перевод сантиметров ==== +constexpr long double operator""_cm_to_m(long double cm) { + return cm / CM_PER_METER; +} + +constexpr long double operator""_cm_to_ft(long double cm) { + return cm / CM_PER_FOOT; +} + +constexpr long double operator""_cm_to_in(long double cm) { + return cm / CM_PER_INCH; +} \ 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..7fa649f8 100644 --- a/01_week/tasks/print_bits/print_bits.cpp +++ b/01_week/tasks/print_bits/print_bits.cpp @@ -1,7 +1,22 @@ -#include -#include +#include +#include void PrintBits(long long value, size_t bytes) { - throw std::runtime_error{"Not implemented"}; + size_t bits = bytes * 8; + unsigned long long bit_index = 1ll << (bits - 1); // 0b1000'...'0000 + size_t delims = 1 + 2 * (bytes - 1); + // Выделяем буфер под биты, апострофы и '\0' в конце + size_t buffer_size = bits + delims + 1; + + std::cout << "0b"; + for (size_t i = 0; i < buffer_size - 1; ++i) { + if (i % 5 == 4) { + std::cout << '\''; + } else { + std::cout << static_cast(static_cast(value & bit_index) + '0'); + bit_index = bit_index >> 1; + } + } + std::cout << std::endl; } diff --git a/01_week/tasks/quadratic/quadratic.cpp b/01_week/tasks/quadratic/quadratic.cpp index abf7d632..73ad53e0 100644 --- a/01_week/tasks/quadratic/quadratic.cpp +++ b/01_week/tasks/quadratic/quadratic.cpp @@ -1,6 +1,37 @@ -#include +#include +#include +#include void SolveQuadratic(int a, int b, int c) { - throw std::runtime_error{"Not implemented"}; + if (a == 0 && b == 0 && c == 0) { + std::cout << "infinite solutions"; + return; + } + + if (a == 0) { + b != 0 ? std::cout << -(static_cast(c) / b) : std::cout << "no solutions"; + return; + } + + if (b == 0 && c == 0) { + std::cout << 0; + return; + } + + double discriminant = static_cast(b) * b - 4.0 * a * c; + + if (discriminant > 0) { + double sqrt_d = std::sqrt(discriminant); + double x1 = (-b - sqrt_d) / (2.0 * a); + double x2 = (-b + sqrt_d) / (2.0 * a); + std::cout << std::setprecision(6) << x1 << ' ' << x2; + } + else if (discriminant == 0) { + double x = -b / (2.0 * a); + std::cout << std::setprecision(6) << x; + } + else { + std::cout << "no solutions"; + } } \ No newline at end of file diff --git a/01_week/tasks/rms/rms.cpp b/01_week/tasks/rms/rms.cpp index 6882f0a9..63daf000 100644 --- a/01_week/tasks/rms/rms.cpp +++ b/01_week/tasks/rms/rms.cpp @@ -1,7 +1,14 @@ -#include -#include +#include double CalculateRMS(double values[], size_t size) { - throw std::runtime_error{"Not implemented"}; + if (size == 0 || values == nullptr) return .0; + + double result = 0; + for (size_t i = 0; i < size; ++i) { + result += values[i]*values[i]; + } + + // size неявно преобразуется к double + return std::sqrt(result / size); } \ No newline at end of file diff --git a/02_week/tasks/func_array/func_array.cpp b/02_week/tasks/func_array/func_array.cpp index b327e68d..1fbd9527 100644 --- a/02_week/tasks/func_array/func_array.cpp +++ b/02_week/tasks/func_array/func_array.cpp @@ -1,6 +1,15 @@ -#include +#include -double ApplyOperations(double a, double b /* other arguments */) { - throw std::runtime_error{"Not implemented"}; +double ApplyOperations(double a, double b, double (*func_arr[])(double, double), size_t size) { + if (size == 0) return 0; + + double result = 0; + + for (size_t i = 0; i < size; ++i) { + if (func_arr[i]) + result += func_arr[i](a, b); + } + + return result; } \ 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..96995c94 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,30 @@ -#include +#include +const int* FindLastElement(const int* begin, const int* end, bool (*predicate)(int)) { + if (!begin || !end || !predicate || begin > end) + return end; -/* return_type */ FindLastElement(/* ptr_type */ begin, /* ptr_type */ end, /* func_type */ predicate) { - throw std::runtime_error{"Not implemented"}; + const int* last = end; + + for (; begin < end; ++begin) { + if (predicate(*begin)) { + last = begin; + } + } + + return last; +} + +int* FindLastElement(int* begin, int* end, bool (*predicate)(int)) { + return const_cast( + FindLastElement( + const_cast(begin), + const_cast(end), + predicate + ) + ); +} + +inline int* FindLastElement(std::nullptr_t, std::nullptr_t, bool (*)(int)) { + return nullptr; } \ 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..f114553c 100644 --- a/02_week/tasks/little_big/little_big.cpp +++ b/02_week/tasks/little_big/little_big.cpp @@ -1,10 +1,24 @@ -#include +#include +#include +void PrintMemory(const u_char* begin, size_t size, bool is_little_endian) { + const u_char* end = begin + size; -void PrintMemory(int /* write arguments here */) { - throw std::runtime_error{"Not implemented"}; + std::cout << "0x" << std::hex << std::uppercase; + for ( + const u_char* ptr = is_little_endian ? end - 1 : begin; + is_little_endian ? ptr >= begin : ptr < end; + is_little_endian ? --ptr : ++ptr + ) { + std::cout << std::setfill('0') << std::setw(2) << static_cast(*ptr); + } + std::cout << std::endl; } -void PrintMemory(double /* write arguments here */) { - throw std::runtime_error{"Not implemented"}; -} \ No newline at end of file +void PrintMemory(int num, bool is_little_endian = false) { + PrintMemory(reinterpret_cast(&num), sizeof(num), is_little_endian); +} + +void PrintMemory(double num, bool is_little_endian = false) { + PrintMemory(reinterpret_cast(&num), sizeof(num), is_little_endian); +} diff --git a/02_week/tasks/longest/longest.cpp b/02_week/tasks/longest/longest.cpp index 04b3c354..be3163e2 100644 --- a/02_week/tasks/longest/longest.cpp +++ b/02_week/tasks/longest/longest.cpp @@ -1,6 +1,42 @@ -#include +#include -/* return_type */ FindLongestSubsequence(/* ptr_type */ begin, /* ptr_type */ end, /* type */ count) { - throw std::runtime_error{"Not implemented"}; +char* FindLongestSubsequence(const char* begin, const char* end, size_t& count) { + if (!begin || !end || begin >= end) { + count = 0; + return nullptr; + } + + int max_len = 1; + int curr_len = 1; + char* sub_begin = nullptr; + char* result = const_cast(begin); + + for (; begin < end - 1; ++begin) { + if (*(begin + 1) == *begin) { + if (sub_begin == nullptr) { + sub_begin = const_cast(begin); + } + ++curr_len; + continue; + } + else if (curr_len > max_len) { + max_len = curr_len; + result = sub_begin; + + } + + curr_len = 1; + sub_begin = nullptr; + } + + if (curr_len > max_len) { + max_len = curr_len; + curr_len = 1; + result = sub_begin; + } + + count = max_len; + + return result; } diff --git a/02_week/tasks/pretty_array/pretty_array.cpp b/02_week/tasks/pretty_array/pretty_array.cpp index 48eab341..d5c17aa9 100644 --- a/02_week/tasks/pretty_array/pretty_array.cpp +++ b/02_week/tasks/pretty_array/pretty_array.cpp @@ -1,6 +1,27 @@ -#include +#include -void PrintArray(/* write arguments here */) { - throw std::runtime_error{"Not implemented"}; +void PrintArray(const int* begin, const int* end, const int threshold = 0) { + if (!begin || !end) { + std::cout << "[]\n"; + return; + } + + std::cout << "["; + for ( + const int* ptr = begin; + begin < end ? ptr < end : ptr > end; + begin < end ? ++ptr : --ptr + ) { + if (threshold && static_cast(ptr - begin) % threshold == 0 && ptr != begin) { + std::cout << "...\n "; + } + + std::cout << *ptr; + + if (ptr != (begin < end ? end - 1 : end + 1)) { + std::cout << ", "; + } + } + std::cout << "]" << std::endl; } \ No newline at end of file diff --git a/02_week/tasks/swap_ptr/swap_ptr.cpp b/02_week/tasks/swap_ptr/swap_ptr.cpp index 93db625d..228e1a68 100644 --- a/02_week/tasks/swap_ptr/swap_ptr.cpp +++ b/02_week/tasks/swap_ptr/swap_ptr.cpp @@ -1,6 +1,5 @@ -#include - - -void SwapPtr(/* write arguments here */) { - throw std::runtime_error{"Not implemented"}; +void SwapPtr(auto*& ptr1, auto*& ptr2) { + auto temp = ptr1; + ptr1 = &*ptr2; + ptr2 = &*temp; } \ No newline at end of file diff --git a/03_week/tasks/data_stats/data_stats.cpp b/03_week/tasks/data_stats/data_stats.cpp index b941c211..0e2dd483 100644 --- a/03_week/tasks/data_stats/data_stats.cpp +++ b/03_week/tasks/data_stats/data_stats.cpp @@ -1,4 +1,6 @@ -#include +#include +#include +#include struct DataStats { @@ -6,6 +8,20 @@ struct DataStats { double sd = 0.0; }; -/* return_type */ CalculateDataStats(/* args */) { - throw std::runtime_error{"Not implemented"}; +DataStats CalculateDataStats(const std::vector& arr) { + DataStats data_stats{}; + + if (arr.empty()) return data_stats; + + // Считаем обычную сумму и сумму квадратов вектора + for (size_t i = 0; i < arr.size(); ++i) { + data_stats.avg += arr[i]; + data_stats.sd += std::pow(arr[i], 2); + } + + // По суммам считаем среднее и sd + data_stats.avg /= arr.size(); + data_stats.sd = std::sqrt(data_stats.sd / arr.size() - std::pow(data_stats.avg, 2)); + + return data_stats; } diff --git a/03_week/tasks/easy_compare/easy_compare.cpp b/03_week/tasks/easy_compare/easy_compare.cpp index dd5cb7f6..36c8cf85 100644 --- a/03_week/tasks/easy_compare/easy_compare.cpp +++ b/03_week/tasks/easy_compare/easy_compare.cpp @@ -1,10 +1,11 @@ -#include - +#include +#include +#include struct Date { - unsigned year; - unsigned month; - unsigned day; + unsigned year = 0u; + unsigned month = 0u; + unsigned day = 0u; }; struct StudentInfo { @@ -13,4 +14,55 @@ struct StudentInfo { int score; unsigned course; Date birth_date; -}; \ No newline at end of file +}; + +bool operator<(const Date& lhs, const Date& rhs) { + return std::tie(lhs.year, lhs.month, lhs.day) < std::tie(rhs.year, rhs.month, rhs.day); +} + +bool operator==(const Date& lhs, const Date& rhs) { + return std::tie(lhs.year, lhs.month, lhs.day) == std::tie(rhs.year, rhs.month, rhs.day); +} + +bool operator!=(const Date& lhs, const Date& rhs) { + return !(lhs == rhs); +} + +bool operator>(const Date& lhs, const Date& rhs) { + return !(lhs < rhs || lhs == rhs); +} + +bool operator>=(const Date& lhs, const Date& rhs) { + return !(lhs < rhs); +} + +bool operator<=(const Date& lhs, const Date& rhs) { + return lhs < rhs || lhs == rhs; +} + +bool operator<(const StudentInfo& lhs, const StudentInfo& rhs) { + return ( + std::tie(rhs.mark, lhs.score, rhs.course, lhs.birth_date) + < std::tie(lhs.mark, rhs.score, lhs.course, rhs.birth_date) + ); +} + +bool operator==(const StudentInfo& lhs, const StudentInfo& rhs) { + return lhs.mark == rhs.mark && lhs.score == rhs.score; +} + +bool operator!=(const StudentInfo& lhs, const StudentInfo& rhs) { + return !(lhs == rhs); +} + +bool operator>(const StudentInfo& lhs, const StudentInfo& rhs) { + return !(lhs < rhs || lhs == rhs); +} + +bool operator>=(const StudentInfo& lhs, const StudentInfo& rhs) { + return !(lhs < rhs); +} + +bool operator<=(const StudentInfo& lhs, const StudentInfo& rhs) { + return lhs < rhs || lhs == rhs; +} \ No newline at end of file diff --git a/03_week/tasks/enum_operators/enum_operators.cpp b/03_week/tasks/enum_operators/enum_operators.cpp index a539be38..38fbaa04 100644 --- a/03_week/tasks/enum_operators/enum_operators.cpp +++ b/03_week/tasks/enum_operators/enum_operators.cpp @@ -1,5 +1,6 @@ -#include -#include +#include +#include +#include enum class CheckFlags : uint8_t { NONE = 0, @@ -12,22 +13,78 @@ enum class CheckFlags : uint8_t { ALL = TIME | DATE | USER | CERT | KEYS | DEST }; -/* return_type */ operator|(/* args */) { - throw std::runtime_error{"Not implemented"}; +std::pair truncate_invalid_flags(CheckFlags lhs, CheckFlags rhs) { + uint8_t all = static_cast(CheckFlags::ALL); + uint8_t left = static_cast(lhs) & all; + uint8_t right = static_cast(rhs) & all; + return std::pair{left, right}; } -/* return_type */ operator&(/* args */) { - throw std::runtime_error{"Not implemented"}; +uint8_t truncate_invalid_flags(uint8_t flags) { + uint8_t all = static_cast(CheckFlags::ALL); + uint8_t flags_cleared = flags & all; + return flags_cleared; } -/* return_type */ operator^(/* args */) { - throw std::runtime_error{"Not implemented"}; +CheckFlags operator|(CheckFlags lhs, CheckFlags rhs) { + auto [left, right] = truncate_invalid_flags(lhs, rhs); + return static_cast(left | right); } -/* return_type */ operator~(/* args */) { - throw std::runtime_error{"Not implemented"}; +bool operator&(CheckFlags lhs, CheckFlags rhs) { + auto [left, right] = truncate_invalid_flags(lhs, rhs); + + if (left == 0u || right == 0u) { + return false; + } + + uint8_t result = left & right; + return (result == left || result == right); +} + +CheckFlags operator^(CheckFlags lhs, CheckFlags rhs) { + auto [left, right] = truncate_invalid_flags(lhs, rhs); + return static_cast(left ^ right); +} + +CheckFlags operator~(CheckFlags flags) { + uint8_t flags_inverted = ~static_cast(flags); + uint8_t flags_cleared = truncate_invalid_flags(flags_inverted); + return static_cast(flags_cleared); } -/* return_type */ operator<<(/* args */) { - throw std::runtime_error{"Not implemented"}; +std::ostream& operator<<(std::ostream& os, CheckFlags flags) { + const static std::pair checks[] { + {CheckFlags::TIME, "TIME"}, + {CheckFlags::DATE, "DATE"}, + {CheckFlags::USER, "USER"}, + {CheckFlags::CERT, "CERT"}, + {CheckFlags::KEYS, "KEYS"}, + {CheckFlags::DEST, "DEST"} + }; + + bool empty = true; + bool first = true; + + for (const auto& [flag, name] : checks) { + uint8_t flags_int = static_cast(flags); + uint8_t check_int = static_cast(flag); + CheckFlags single_flag = static_cast(flags_int & check_int); + if (single_flag != CheckFlags::NONE) { + if (first) { + first = false; + } + else { + os << ", "; + } + os << name; + empty = false; + } + } + + if (empty) { + os << "NONE"; + } + + return os; } diff --git a/03_week/tasks/filter/filter.cpp b/03_week/tasks/filter/filter.cpp index 6648cb39..86e7855a 100644 --- a/03_week/tasks/filter/filter.cpp +++ b/03_week/tasks/filter/filter.cpp @@ -1,6 +1,18 @@ -#include +#include +#include -/* return_type */ Filter(/* args */) { - throw std::runtime_error{"Not implemented"}; +void Filter(std::vector& arr, bool (*filter_func)(int)) { + if (!filter_func) return; + + size_t j = 0; + + for (size_t i = 0; i < arr.size(); ++i) { + if (filter_func(arr[i])) { + arr[j++] = arr[i]; + } + } + + size_t new_size = j; + arr.resize(new_size); } \ No newline at end of file diff --git a/03_week/tasks/find_all/find_all.cpp b/03_week/tasks/find_all/find_all.cpp index 74f393b2..9f088ff4 100644 --- a/03_week/tasks/find_all/find_all.cpp +++ b/03_week/tasks/find_all/find_all.cpp @@ -1,6 +1,20 @@ -#include +#include +#include -/* return_type */ FindAll(/* args */) { - throw std::runtime_error{"Not implemented"}; +std::vector FindAll(const std::vector& arr, bool (*bool_func)(int)) { + std::vector result{}; + + if (!bool_func) return result; + + result.reserve(arr.size()); + + for (size_t i = 0; i < arr.size(); ++i) { + if (bool_func(arr[i])) { + result.push_back(i); + } + } + + result.shrink_to_fit(); + return result; } \ No newline at end of file diff --git a/03_week/tasks/minmax/minmax.cpp b/03_week/tasks/minmax/minmax.cpp index c2869799..adc13ccc 100644 --- a/03_week/tasks/minmax/minmax.cpp +++ b/03_week/tasks/minmax/minmax.cpp @@ -1,6 +1,29 @@ -#include +#include +typedef std::pair::const_iterator, std::vector::const_iterator> MinMaxIter; -/* return_type */ MinMax(/* args */) { - throw std::runtime_error{"Not implemented"}; +MinMaxIter MinMax(const std::vector& arr) { + auto first = arr.begin(), last = arr.end(); + + if (first == last) + return {last, last}; + + auto min = first, max = first; + + while (++first != last) { + auto i = first; + if (++first == last) { + min = (*i < *min) ? i : min; + max = (*i >= *max) ? i : max; + break; + } + + auto smaller = (*first < *i) ? first : i; + auto larger = (*first < *i) ? i : first; + + min = (*smaller < *min) ? smaller : min; + max = (*larger >= *max) ? larger : max; + } + + return {min, max}; } diff --git a/03_week/tasks/os_overload/os_overload.cpp b/03_week/tasks/os_overload/os_overload.cpp index e473418d..86d3b8d2 100644 --- a/03_week/tasks/os_overload/os_overload.cpp +++ b/03_week/tasks/os_overload/os_overload.cpp @@ -1,21 +1,49 @@ -#include #include #include +#include struct Coord2D { - int x; - int y; + int x = 0; + int y = 0; }; struct Circle { - Coord2D coord; - unsigned radius; + Coord2D coord = {}; + unsigned radius = 1; }; using CircleRegion = std::pair; using CircleRegionList = std::vector; -/* return_type */ operator<<(/* args */) { - throw std::runtime_error{"Not implemented"}; +std::ostream& operator<<(std::ostream& lhs, const Coord2D& rhs) { + return lhs << '(' << rhs.x << ", " << rhs.y << ')'; +} + +std::ostream& operator<<(std::ostream& lhs, const Circle& rhs) { + if (rhs.radius == 0) { + return lhs << "circle[]"; + } + + return lhs << "circle[" << rhs.coord << ", r = " << rhs.radius << "]"; +} + +std::ostream& operator<<(std::ostream& lhs, const CircleRegion& rhs) { + return lhs << (rhs.second ? '+' : '-') << rhs.first; +} + +std::ostream& operator<<(std::ostream& lhs, const CircleRegionList& rhs) { + lhs << "{"; + + if (!rhs.empty()) { + lhs << "\n\t" << rhs[0]; + } + + for (size_t i = 1; i < rhs.size(); ++i) { + lhs << ",\n\t" << rhs[i]; + } + + lhs << (rhs.empty() ? "}" : "\n}"); + + return lhs; } diff --git a/03_week/tasks/range/range.cpp b/03_week/tasks/range/range.cpp index d2085495..9869410a 100644 --- a/03_week/tasks/range/range.cpp +++ b/03_week/tasks/range/range.cpp @@ -1,7 +1,16 @@ -#include #include +#include -std::vector Range(int from, int to, int step) { - throw std::runtime_error{"Not implemented"}; +std::vector Range(int from, int to, const int step = 1) { + std::vector result{}; + result.reserve(std::abs(from - to)); + + while ((from > to && step < 0) || (from < to && step > 0)) { + result.push_back(from); + from += step; + } + + result.shrink_to_fit(); + return result; } diff --git a/03_week/tasks/unique/unique.cpp b/03_week/tasks/unique/unique.cpp index 9d2545bb..291e4733 100644 --- a/03_week/tasks/unique/unique.cpp +++ b/03_week/tasks/unique/unique.cpp @@ -1,6 +1,17 @@ -#include #include +#include -/* return_type */ Unique(/* args */) { - throw std::runtime_error{"Not implemented"}; + +std::vector Unique(const std::vector& arr) { + std::vector unique{}; + unique.reserve(arr.size()); + + for (size_t i = 0; i < arr.size(); ++i) { + if (unique.empty() || unique[unique.size() - 1] != arr[i]) { + unique.push_back(arr[i]); + } + } + + unique.shrink_to_fit(); + return unique; } diff --git a/04_week/04_class.md b/04_week/04_class.md deleted file mode 100644 index 46e00076..00000000 --- a/04_week/04_class.md +++ /dev/null @@ -1,902 +0,0 @@ -# Лекция 4. ООП. Класс - -1. [ООП](#oop) - - [Инкапсуляция](#encapsulation) - - [Наследование](#inheritance) - - [Полиморфизм](#polymorphism) - - [Абстракция](#abstraction) -1. [Класс](#class) - - [Спецификаторы доступа](#access_specifiers) - - [Отличие класса и структуры](#class_struct_diff) - - [Пустой класс](#class_empty) - - [Поля класса](#class_fields) - - [Инициализация полей значением по умолчанию](#field_default_init) - - [Конструктор](#ctor) - - [Список инициализации полей класса](#member_initializer_list) - - [Параметризованный конструктор](#parameterized_ctor) - - [Конструктор по умолчанию](#default_ctor) - - [Конструктор копирования](#copy_ctor) - - [Указатель на себя `this`](#this_ptr) - - [Копирующий оператор присваивания](#copy_assignment) - - [Деструктор](#dtor) - - [Конструктор преобразования](#converting_ctor) - - [Ключевое слово `explicit`](#explicit) - - [Конструктор от `std::initializer_list`(_C++11_)](#ctor_std_initializer_list) - - [Делегирующий конструктор (_C++11_)](#delegating_ctor) - - [Ключевое слово `default` (_С++11_)](#keyword_default) - - [Ключевое слово `delete` (_С++11_)](#keyword_delete) - - [Методы](#methods) - - [Определение методов вне класса](#methods_definition_outside) - - [CV-квалификация методов ](#cv_for_methods) - - [Оператор преобразования](#conversion_operator) - - [Перегрузка операторов внутри класса](#class_operator_overloading_inside) - - [Перегрузка операторов вне класса](#class_operator_overloading_outside) - - [Ключевое слово `friend`](#keyword_friend) - - [Ключевое слово `mutable`](#keyword_mutable) - - -## ООП - -Объектно-ориентированное программирование - парадигма программирования, которая -основывается на представление в коде программы различных объектов, взаимодействующих -друг с другом. - -Класс - пользовательский тип данных, шаблон (макет) для создания объектов и описания -их характеристик, функций. - -Объект - экземпляр класса. Объект включает данные (поля) и методы (функции). -Что позволяет хранить характеристики объекта, изменять их и взаимодействовать с -другими объектами. - -Основные принципы ООП: - -- Инкапсуляция -- Наследование -- Полиморфизм -- Абстракция - -### Инкапсуляция - -Инкапсуляция - объединение данных и методов для работы с данными внутри класса. -Сокрытие деталей реализации класса. - -```c++ -class Budget { -public: - void increase_balance(double value) { - budget_ += value; - } -private: - double budget_; -}; -``` - -### Наследование - -Наследование - механизм создания новых классов на основе существующих. Позволяет -строить иерархию классов и переиспользовать код классов родителей внутри -классов наследников. - -```c++ -class Animal { /* common data */}; -class Cat : public Animal {}; -class Dog : public Animal {}; -``` - -### Полиморфизм - -Полиморфизм - возможность различного поведения сущностей C++. - -Виды полиморфизма: - -- статический (на этапе компиляции, шаблоны, перегрузка функций) -- динамический (во время выполнения программы, виртуальные методы) - -```c++ -class Shape { -public: - virtual void draw() = 0; -}; -class Circle : public Shape { - void draw() override { /* рисуем круг */ } -}; -``` - -### Абстракция - -Абстракция - упрощение сложных вещей через выделение основных характеристик. - -## Класс - -Класс - пользовательский тип данных, который объединяет в себе данные (поля класса) -и функции для работы с данными (методы класса), представляет собой макет для -создания объектов (экземпляров) данного типа. - -Синтаксис: `class {};` - -- `` - имя класса, пользовательского типа данных -- `` - тело класса, включающее поля, методы, конструкторы и деструктор - -### Спецификаторы доступа - -Для ограничения видимости полей и методов внутри класса используются -спецификаторы доступа, весь код после спецификатора имеет соответствующий тип -доступа: - -- `public` - публичный доступ, поле или метод класса доступны извне -- `protected` - защищенный доступ, поля и методы доступны наследникам класса - и внутри класса -- `private` - приватный доступ, поля и методы доступны только внутри класса. - -Синтаксис внутри класса или структуры: `:` - -Указывать спецификаторы доступа можно произвольное число раз. - -```c++ -class User { -public: - /* some data and functions for everyone */ -protected: - /* some data and functions for children classes */ -private: - /* some data and functions inside class */ -}; -``` - -Приватные поля и методы будут недоступны снаружи, то есть **НЕЛЬЗЯ** к ним -обратится или вызвать через экземляр класс, используя операторы `.`, `->`. - -Всё содержимое класса по умолчанию имеет спецификатор доступа `private`, -несмотря на это часто принято явно указывать данный спецификатор, даже при -определении полей класса в самом начале тела класса. - -### Отличие класса и структуры - -Структура `struct` и класс `class` имеют одинаковые возможности в C++. - -Отличие заключается, что содержимое структуры по умолчанию имеет публичный -доступ `public`, а содержимое класса приватный `private` - -Структура нужна для взаимодействия с `legacy` кодом на языке C, а также для -публичных классов. - -Несмотря на одинаковые возможности, принято разделять структуру и класс -семантически. Так, структуру используют только с публичными полями, а класс -с приватными. Создавать классы и структуры со смешанным типом полей не -рекомендуется, так как это может быть не очевидно и не понятно программистам, -читающим код. - -### Пустой класс - -Пустой класс имеет размер 1 байт, поскольку объект такого класса можно создать -и необходимо иметь адресс данного объекта, чтобы иметь адресс, необходимо что-то -положить в память по определенному адресу. - -Чаще используется пустая структура. Такая структура может понадобитсья в качестве -именнованного тега. Пока будем просто считать, что иногда надо. - -### Поля класса - -Поля класса представляют собой внутренние переменные произвольного типа. -К полям класса внутри класса можно обращаться по имени. В качестве поля можно -использовать указатели и ссылки. - -В случае ссылок необходимо их инициализировоать при создании объекта. Например, -можно проинициализирвоать адресом объекта из глобальной области видимости. А еще -это можно сделать в списке инициализации при конструировании объекта. - -Существуют разные стили кода к именованию полей класса. Часто встречается: - -- `m_` - добавляют `m_` в качестве префикса к перименной (`m` - `member`) -- `_` - добавляют `_` в качестве постфикса к переменной. - -```c++ -// inside class -int m_value; -int value_; -``` - -Поля класса хранятся в классе и инициализируются в порядке их объявления. - -Поля уникальны для каждого экземпляра класса. - -### Инициализация полей значением по умолчанию - -Аналогично структурам рекомендуется всегда инициализировать поля внутри класса. - -```c++ -class Time { -private: - int hour_ = 0; - int minute_{0}; // uniform -}; -``` - -Иначе в полях класса также может лежать мусор. - -### Конструктор - -Конструктор это особый метод класса, который используется для конструирования -объекта. - -Синтаксис: `() {}` - -- `` - имя конструктора должно совпадать с именем класса -- `` - аргументы конструктора. -- `` - тело конструктора. - -В зависимости от аргументов конструктора выделяют различные типы конструктора. -Основные способы конструирования объекта: - -- Параметризованный конструктор -- Конструктор по умолчанию -- Конструктор копирования -- Копирующий оператор присваивания -- Конструктор перемещения -- Перемещающий оператор присваивания - -Важно понимать, что если конструкторы не определены, то компилятор самостоятельно -сгенерирует конструкторы. Но если определен, хотя бы один конструктор, то -компилятор скорее всего этого не сделает. - -Важно понимать, что при входе в тело конструктора все поля уже проинициализированы -и в теле может происходить только присваивание новых значений полям класса. -Следовательно, в теле уже нельзя изменить константное поле или инициализировать -ссылку. - -Проинициализировать константу и ссылку можно не только значением по -умолчанию или значением (адресом) переменной из глобальной области видимости. -Для этого в синтаксисе конструктора предусмотрен список инициализации. - -### Список инициализации полей класса - -Список инициализации полей (_member initializer list_) позволяет инициализировать -поля в момент создания объекта. В списке инициализации доступны аргументы -конструктора и имена полей класса. Список инициализации указывается между сигнатурой -и телом конструктора, и выглядит как перечисление после символа `:` через запятую -полей класса и внутри `()` или `{}` их инициализирующих значений. - -Синтаксис: `() : {}` - -- `` - список инициализации = `(), {}` - -```c++ -class InitList { -public: - InitList(int val) : a_(val), b_(val), c_(val) {} -public: - int a_; int b_; int c_; -}; -``` - -Причем имена полей класса и имена параметров могут полностью совпадать, конфликта -имен не будет, поскольку компилятор понимает, что нужно инициализировать поля. - -```c++ -class InitSameName { -public: - InitSameName(int a, int b, int c) : a(a), b(b), c(c) {} -public: - int a; int b; int c; -}; -``` - -Также, следует отметить, что в качестве инициализирующего значения, может использоваться -не только переменная или константа, но также выражение (_expression_) и результат -вызова функции. - -**ВАЖНО**, что инициализация происходит в порядке полей класса, и не зависит от -порядка в списке инициализации. Поэтому важно самостоятельно отслеживать -правильный порядок инициализации. - -```c++ -class BadOrderInit { -public: - BadOrderInit(int val) : c(val), b(c + 1), a(10) {} -public: - int a; int b; int c; -}; -``` -- `c` используется неинициализированной при инициализации `b` (**UB**) - -Если поля класса объявлены со значением по умолчанию, то они будут проигнорированы -для полей в списке инициализации. - -```c++ -class BadOrderInit { -public: - BadOrderInit(int val) : c(val), b(c + 1), a(10) {} -public: - int a = 7; int b = 7; int c = 7; -}; -``` -- значение `7` будет проигнорированно, по-прежнему **UB** - -Списки инициализации могут быть неполными. Тогда недостающие поля будут -сконструированы со значениями по умолчанию, а при их отсутствии инициализируются -мусором. - -```c++ -class BadOrderInitNoAB { -public: - BadOrderInitNoAB(int val) : c(val) {} -public: - int a; int b = 7; int c = 7; -}; -``` -- в поле `b` будет значение `7`, в `a` будет мусор - -Список инициализации позволяет сконструировать константное поле и поле ссылку извне: - -```c++ -class RefConst { -public: - RefConst(int value, int& ref, const double& cref) - : id_(value), ref_(ref), const_ref_(cref) {} -private: - const int id_; - int& ref_; - const double& const_ref_; -}; -``` - -### Параметризованный конструктор - -Конструктор, который имеет параметры (аргументы) называют параметризованным -конструктором (конструктором с параметрами). Аргументов может быть несколько -и они могут иметь значения по умолчанию, Таким образом, конструктор может -быть перегружен. - -```c++ -class Time { -public: - Time(int hour, int minute, int second) - : hour_(hour), minute_(minute), second_(second) {} -private: - int hour_, minute_, second_; -}; -``` - -Если конструктор имеет у всех аргументов значение по умолчанию, то такой -конструктор перегружает конструктор по умолчанию. - -```c++ -class Time { -public: - Time(int hour = 0, int minute = 0, int second = 0) - : hour_(hour), minute_(minute), second_(second) {} -private: - int hour_, minute_, second_; -}; -``` - -Для создания объекта класса необходим вызов конструктора. Синтаксис вызова констуктора: - -```c++ -Time t1(1, 1, 1); -Time t2{1, 1, 1}; -Time t3 = {1, 1, 1} -Time t4 = Time{1, 1, 1}; -Time t5 = Time(1, 1, 1); -``` - -Аналогично для всех его вариантов перегрузки. - -### Конструктор по умолчанию - -Конструктор по умолчанию представляет собой конструктор без аргументов. -Часто для простых класов конструктор имеет пустое тело. Удобно использовать -значение по умолчанию для инициализации. - -Синтаксис: `() {}` - -Часто имеет пустое тело для тривиальных случаев. - -Если не определен ни один конструктор, то компилятор самостоятельно сгенерирует -данный конструктор. - -```c++ -class DefaultCtor { -public: - DefaultCtor() {} -private: - int value = 0; -}; -``` - -Вызов конструктора: - -```c++ -DefaultCtor obj; -DefaultCtor obj2{}; -DefaultCtor obj3 = {}; -DefaultCtor obj4 = DefaultCtor{}; -DefaultCtor obj5 = DefaultCtor(); -``` -- во всех этих случаях вызовется только конструктор по умолчанию один раз - -### Конструктор копирования - -Конструктор копирования необходим для создания копии объекта из объекта того -же типа. Представляет собой конструктор, принимающий в качестве аргументов -константную ссылку того же типа, что и сам класс. - -Синтаксис: `(const & ) {}` - -```c++ -class Time { -public: - Time(const Time& other) - : hour_(other.hour_), minute_(other.minute_), second_(other.second_) {} -private: - int hour_, minute_, second_; -}; -``` - -Поля другого объекта того же класса доступны внутри методов класса даже если они -приватные. - -Вызывается конструктор копирования: - -- при передаче в функцию по значению -- при возврате объекта соответствующего значения по значению -- при конструировании одного объекта из другого - -```c++ -Time t; -Time t1 = t; // copy ctor -Time t2(t); // copy ctor -Time t3{t}; // copy ctor -``` - -### Указатель на себя `this` - -Внутри класса, в методах, в том числе конструкторах, можно получить указатель на -себя (объект класса, который вызывает данный метод) с помощью ключевого слова `this`. - -Можно использовать `this`, как в качестве значения по умолчанию, так и в списке -инициализации. - -```c++ -class Self { -public: - Self() : self(this) {}; - Self* GetPtr() { return self; } - Self& GetRef() { return *this; } -private: - Self* self = this; -}; -``` - -Можно считать что указатель на себя передается первым неявным аргументом в конструкторы, -методы и операторы класса. - -Через указатель можно явно обращаться к полям класса, но как правило, так не делают - -```c++ -// inside methods -this->self; -``` - -### Копирующий оператор присваивания - -Оператор присвания необходим при присваивании одного созданного объекта другому. -Если один из объектов не создан, то он не будет вызываться, а будет вызываться -конструктор копирования, даже если в инструкции есть `=`. - -Как правило, оператор возвращает ссылку на себя (экземпляр текущего класса), что -позволяет испоьзовать цепочку из операторов `=`. Для этого необходимо вернуть из -оператора разыменованный указатель на себя `return *this;`. - -Синтаксис: `& operator=(const & ) {}` - -Поскольку язык не запрещает присвоить объект самому себе, как правило, в копирующем -операторе присваивания выполняют проверку на самоприсваивание. Особенно это -критично для классов владеющих ресурсами (выделяющих память), что может привести -к **UB** - -```c++ -class Time { -public: - Time& operator=(const Time& other) { - if (this == &other) { - return *this; - } - hour_ = other.hour_; - minute_ = other.minute_; - second_ = other.second_; - return *this; - } -private: - int hour_, minute_, second_; -}; -``` - -Вызов оператора: - -```c++ -Time t1, t2, t3; -t1 = t2; // copy assignment -t1 = t1; // copy assignment -t1 = t2 = t3; // copy assignment -auto t4 = t1; // copy ctor (not a copy assignment!) -``` - -### Деструктор - -Особый метод, вызываемый перед разрушением объекта, когда заканчивается время -жизни объекта. - -Синтаксис: `~() {}` - -Если в конструкторе выполнялось ручное выделение ресурсов, то в деструкторе -необходимо обязательно освободить ресурсы. Иначе деструктор остается тривиальным -и генерируется компилятором по умолчанию. - -Деструкторы вызываются в обратном порядке по отношению к конструируемым объектам -при выходе из области видимости. Последний сконструированный объект, будет разрушен -первым. - -### Конструктор преобразования - -Конструктором преобразования называется конструктор, принимающий один аргумент -другого произвольного типа. Данный конструктор разрешает неявное преобразование -из указанного типа в тип класса. - -```c++ -class Integer { -private: - int value; -public: - Integer(int v) : value(v) {} - Integer(char c) : value(static_cast(c)) {} -}; -``` - -Таким образом, если функция принимает пользовательский класс, а класс имеет -конструктор преобразования от другого типа, то в функцию можно передать -непосредственно этот другой тип, произойдет неявное преобразование с помощью -соответствующего конструктора: - -```c++ -int DoSomething(Integer i) {} - -int main() { - Integer i{3}; - int value = 5; - char c = 'I'; - DoSomething(i); // OK - DoSomething(value); // OK - DoSomething(5); // OK - DoSomething(c); // OK - DoSomething('i'); // OK -} -``` - -**ВАЖНО** понимать, что при наличии конструктора присваивания из другого типа, -компилятор **НЕ** будет генеировать оператор присваивания из данного типа, его -необходимо определять самостоятельно. - -### Ключевое слово `explicit` - -Ключевое слово `explicit` используется как спецификатор перед именем конструктора -и позволяет запретить неявное преобразование и сообщает компилятору, что данный -конструктор можно вызывать только явно. - -```c++ -class Integer { -private: - int value; -public: - Integer(int v) : value(v) {} - Integer(char c) : value(static_cast(c)) {} - explicit Integer(double d) : value(static_cast(d)) {} -}; -``` - -Неявно такой конструктор вызвать нельзя: - -```c++ -//Integer i2 = 3.14; // compile error -Integer i3 = Integer{3.14}; // OK - -int DoSomething(Integer i) {} - -int main() { - double d = 3.14; - //DoSomething(d); // compile error - //DoSomething(3.14); // compile error - DoSomething(Integer{3.14}); // OK - DoSomething(Integer(3.14)); // OK -} -``` - -Также спецификатор `explicit` можно использовать с оператором преобразования, об этом -после знакомства с методами. - -### Конструктор от `std::initializer_list`(_C++11_) - -В C++11 появился контейнер список инициализации `std::initializer_list`, который -позволяет инициализировать класс набором элементов. Что вызывает неоднозначность -при наличии параметризированных конструкторов какой конструктор вызывать. - -Конструктор по умолчанию имеет приоритет перед конструктором от списка инициализации. - -Список инициализации имеет приоритет перед параметризированными конструкторами при -использовании `{}`. - -```c++ -class Vector { -public: - Vector() {}; - Vector(size_t count); - Vector(int a, int b); - Vector(std::initializer_list list); -private: - std::vector data; -}; -``` - -Вызов конструкторов: - -```c++ -Vector v = {1, 2, 3, 4, 5}; // ctor std::initializer_list -Vector v2{1, 2, 3}; // ctor std::initializer_list -Vector v3(10); // ctor Vector(size_t) -Vector v4{10}; // ctor std::initializer_list -Vector v5 = {10}; // ctor std::initializer_list -Vector v6(10, 20); // ctor Vector(int a, int b) -Vector v7{10, 20}; // ctor std::initializer_list -Vector v8 = {10, 20}; // ctor std::initializer_list -Vector v9 = 10; // ctor Vector(size_t) implicit cast -Vector v10; // default ctor -Vector v11{}; // default ctor -Vector v12 = {}; // default ctor -``` - -### Делегирующий конструктор (_C++11_) - -Делегирующий конструктор - конструктор, который на месте списка инициализации -использует другой конструктор данного класса. В таком случае можно указать только -один целевой конструктор, дополнительно списки инициализации указать нельзя. - -```c++ -class Time { -public: - Time(int hour, int minute, int second) - : hour_(hour), minute_(minute), second_(second) {} - Time(int hour) : Time(hour, 0, 0) {} -private: - int hour_, minute_, second_; -}; -``` - -Делегирующий конструктор **НЕ** может быть рекурсивным. - -### Ключевое слово `default` (_С++11_) - -С помощью ключевого слова `default` можно явно попросить компилятор сгенерировать -конструктор (деструктор), указав после сигнатуры `= default`. Это более выразительно, -чем писать `{}` для конструктора по умолчанию. Рекомендуется к использованию. - -```c++ -class Value { -public: - Value(int x) : x_(x) {} - Value() = default; - Value(const Value& other) = default; - Value(Value&& other) = default; - Value& operator=(const Value& other) = default; - Value& operator=(Value&& other) = default; - ~Value() = default; -private: - int x = 0; -}; -``` - -### Ключевое слово `delete` (_С++11_) - -С помощью ключевого слова `delete` можно явно попросить компилятор удалить функцию -(запретить её использование), указав после сигнатуры `= delete`. Это более выразительно, -чем прятать конструкторы в приватную область класса. Рекомендуется к использованию. - -Можно использовать не только для конструкторов, деструкторов, но и для любых методов, -операторов, шаблонных функций, функций вне классов. - -```c++ -class Value { -public: - Value(int x) : x_(x) {} - Value() = delete; - Value(const Value&) = delete; - Value& operator=(const Value&) = delete; -private: - int x = 0; -}; -``` - -Например, если класс не подразумевает сравнения на равенство или других операторов -можно явно указать для них `delete`. - -### Методы - -Внутри класса можно определять функции, которые могут работать с полями класса, в том -числе закрытыми. Данные функции называются методы. - -Синтаксис аналогичен определению обычным функциям. - -Публичный метод можно вызвать через операторы `.` для экземпляра и `->` для указателяю. - -Приватные методы, можно вызывать внутри класса. - -Можно вызывать методы в списках инициализации. Например, метод, который будет -контролировать допустимость значения или выполнять дополнительные преобразования. - -### Определение методов вне класса - -Методы можно объявить внутри класса, а определить снаружи класса. Содержимое -класса имеет свою область видимости. Для определения снаружи класса перед именем -конструктора, метода, оператора используется имя класса и оператор разрешения области -видимости `::` - -```c++ -class Time { -public: - Time(); - Time(int hours, int minutes, int seconds = 0); - int GetHours(); - void SetHours(int hours); -private: - int hours_ = 0; - int minutes_ = 0; - int seconds_ = 0; -}; - -Time::Time() = default; -Time::Time(int hours, int minutes, int seconds) - : hours_(hours), minutes_(minutes), seconds_(seconds) {} - -int Time::GetHours() { return hours; } -void Time::SetHours(int hours) { hours_ += hours; } -``` - -Аргументы методов, имеющие значения по умолчанию указываются только при объявлении, -при определении нельзя указать значения по умолчанию - -### CV-квалификация методов - -Методы могут иметь CV-квалификацию. Методы, которые не изменяют полей класса, а только -предоставляют информацию о них следует помечать квалификатором `const` после сигнатуры -и перед телом метода: - -```c++ -class Size { -public: - size_t GetSize() const { return size; }; - void AddSize(size_t size) { size_ += size; }; -private: - size_t size_ = 0; -}; -``` - -Методы помеченные квалификатором `const` можно вызывать у константных объектов класса. -Компилятор отслеживает, что в данном методе нет измененеий полей класса. - -Методы можно перегрузить только по квалификатору `const`. - -Не изменяет поля класса и может быть вызван для константного объекта: - -```c++ -int Class::foo() const; -``` - -Может изменять поля класса и может быть вызван для `volatile` объекта: - -```c++ -int Class::foo() volatile; -``` - -Может быть вызван как для `const`, так и для `volatile` объекта, так и для -`const volatile` объекта: - -```c++ -int Class::foo() const volatile; -``` - -### Оператор преобразования - -В классе возможно определить оператор преобразования, который позволяет преобразовывать -пользовательский класс в другой тип. - -Синтаксис: ` operator () const {}` - -- `` - можно запретить неявное преобразование -- `` - тип к которому выполняется приведение - -Рекомендуется помечать `const` поскольку данный оператор не должен менять полей класса -и вызываться от констант данного класса. - -Как правило рекомендуется запрещать неявное преобразование к типу (использовать -`explicit`), поскольку можно обнаружить много неожиданных мест в коде, где неявно -произведено преобразование. - -Исключением обычно является оператор `bool` для удобства использования в условиях. - -### Перегрузка операторов внутри класса - -Поскольку первым аргументом неявно передается ключевое слово `this`, то перегрузка -бинарных операторов внутри класса имеет один аргумент: - -```c++ -Class& operator+=(const Class& other); -Class& operator-=(const Class& other); -Class& operator*=(const Class& other); -Class& operator/=(const Class& other); -``` - -Операторы арифмесстических операций часто переопределяют на основе работы присваивающих -операторов: - -```c++ -Class operator+(const Class& other) const { - Class result = *this; // copy ctor - result += other; // operator += - return result; -} -``` - -Операторы префиксного и постфиксного инкремента/декремента переопределяются -следующим образом: - -```c++ -Class& operator++(); // ++obj -Class operator++(int); // obj++ -Class& operator--(); // --obj -Class operator--(int); // obj-- -``` -- постфиксный оператор возвращает копию, поэтому у возвращаемого значения нет `&` - -### Перегрузка операторов вне класса - -Операторы можно перегрузить вне класса, тогда сигнатура перегружаемого оператора -пишется в привычной манере. Но для реализации таких операторов у класса должны быть -методы задающие и считывающие значение полей (геттеры и сеттеры). Бывает, что их нет, -тогда перегрузить класс не получится или получится на основе определенных операторов -составного присваивания внутри класса. - -Перегрузка инкремента и декремента вне класса: - -```c++ -Class& operator++(const Class& obj); // ++obj -Class operator++(const Class& obj, int); // obj++ -Class& operator--(const Class& obj); // --obj -Class operator--(const Class& obj, int); // obj-- -``` - -### Ключевое слово `friend` - -Внутри класса с помощью ключевого слова `friend` (_friend declaration_) можно -объявить дружественную функцию, класс или дружественный метод другого класса. - -Сущности объявленные дружественными будут иметь доступ к `private` и `protected` -полям класса. - -Дружественность работает в одностороннем порядке. - -```c++ -friend void SomeMethod(int); -friend struct SomeStruct; -friend class SomeClass; -friend OtherClass; // C++11 -friend int OtherClass::Method(); -``` - -### Ключевое слово `mutable` - -Спецификатор типа `mutable` разрешает изменять поле класса, объявленное с ним, -даже в константных методах и для константных объектов. - -Например, это может быть поле представляющее собой счетчик операций и необходимо его -изменять даже в константном методе. - -Также может использоваться в лямбда-выражениях \ No newline at end of file diff --git a/04_week/04_class.pdf b/04_week/04_class.pdf deleted file mode 100644 index 50a488d9..00000000 Binary files a/04_week/04_class.pdf and /dev/null differ diff --git a/04_week/tasks/phasor/phasor.cpp b/04_week/tasks/phasor/phasor.cpp index 3ec1b9ad..177820d5 100644 --- a/04_week/tasks/phasor/phasor.cpp +++ b/04_week/tasks/phasor/phasor.cpp @@ -1,10 +1,206 @@ - +#include +#include +#include struct ExpTag {}; struct DegTag {}; struct AlgTag {}; +double areDoublesEqual(const double a, const double b, const double eps = 1e-12) { + return std::abs(a - b) < eps; +} class Phasor { - +public: + Phasor(); + Phasor(const double m, const double phi); + Phasor(const double m, const double phi, const ExpTag& tag); + Phasor(const double m, const double deg, const DegTag& tag); + Phasor(const double x, const double y, const AlgTag& tag); + void SetPolar(const double m, const double phi); + void SetCartesian(const double x, const double y); + double Magnitude() const; + double Abs() const; + double Phase() const; + double Angle() const; + double PhaseDeg() const; + double AngleDeg() const; + double Real() const; + double Imag() const; + Phasor Conj() const; + Phasor Inv() const; + bool operator==(const Phasor& other) const; + bool operator!=(const Phasor& other) const; + Phasor operator-() const; + Phasor& operator+=(const Phasor& other); + Phasor& operator-=(const Phasor& other); + Phasor& operator/=(const Phasor& other); + Phasor& operator*=(const Phasor& other); +private: + double m_m = .0; + double m_phi = .0; + double m_deg_to_rad(const double deg) const; + double m_rad_to_deg(const double deg) const; + void m_normalize_magnitude(); + void m_normalize_phase(); + void m_normalize_phasor(); }; + +double Phasor::m_deg_to_rad(const double deg) const { + return deg * M_PI / 180; +} + +double Phasor::m_rad_to_deg(const double rad) const { + return rad * 180 / M_PI; +} + +void Phasor::m_normalize_magnitude() { + if (m_m < 0) { + m_m = std::abs(m_m); + m_phi += (m_phi > 0) ? -M_PI : M_PI; + } +} + +void Phasor::m_normalize_phase() { + m_phi = std::atan2(std::sin(m_phi), std::cos(m_phi)); + + if (m_phi <= -M_PI) { + m_phi = M_PI; + } +} + +void Phasor::m_normalize_phasor() { + m_normalize_magnitude(); + m_normalize_phase(); +} + +void Phasor::SetCartesian(const double x, const double y) { + m_m = sqrt(pow(x, 2) + pow(y, 2)); + m_phi = atan2(y, x); + m_normalize_phasor(); +} + +Phasor::Phasor() {} +Phasor::Phasor(const double m, const double phi = 0) : m_m(m), m_phi(phi) { + m_normalize_phasor(); + +} +Phasor::Phasor(const double m, const double phi, const ExpTag&) : Phasor(m, phi) {} +Phasor::Phasor(const double m, const double deg, const DegTag&) : Phasor(m, m_deg_to_rad(deg)) {} +Phasor::Phasor(const double x, const double y, const AlgTag&) { + SetCartesian(x, y); +} + +void Phasor::SetPolar(const double m, const double phi) { + m_m = m; + m_phi = phi; + m_normalize_phasor(); +} + +double Phasor::Magnitude() const { + return m_m; +} + +double Phasor::Abs() const { + return m_m; +} + +double Phasor::Phase() const { + return m_phi; +} + +double Phasor::Angle() const { + return m_phi; +} + +double Phasor::PhaseDeg() const { + return m_rad_to_deg(m_phi); +} + +double Phasor::AngleDeg() const { + return m_rad_to_deg(m_phi); +} + +double Phasor::Real() const { + return m_m * cos(m_phi); +} + +double Phasor::Imag() const { + return m_m * sin(m_phi); +} + +Phasor Phasor::Conj() const { + return Phasor(m_m, -m_phi); +} + +Phasor Phasor::Inv() const { + return Phasor(1 / m_m, -m_phi); +} + +bool Phasor::operator==(const Phasor& other) const { + return areDoublesEqual(m_m, other.m_m) && areDoublesEqual(m_phi, other.m_phi); +} + +bool Phasor::operator!=(const Phasor& other) const { + return !(*this == other); +} + +Phasor Phasor::operator-() const { + return Phasor(-m_m, m_phi); +} + +Phasor& Phasor::operator+=(const Phasor& other) { + SetCartesian(Real() + other.Real(), Imag() + other.Imag()); + return *this; +} + +Phasor& Phasor::operator-=(const Phasor& other) { + SetCartesian(Real() - other.Real(), Imag() - other.Imag()); + return *this; +} + +Phasor& Phasor::operator*=(const Phasor& other) { + SetPolar(m_m * other.m_m, m_phi + other.m_phi); + return *this; +} + +Phasor& Phasor::operator/=(const Phasor& other) { + SetPolar(m_m / other.m_m, m_phi - other.m_phi); + return *this; +} + +std::ostream& operator<<(std::ostream& os, const Phasor& phasor) { + return ( + os << std::fixed << std::setprecision(3) << phasor.Magnitude() + << "*e(j*" << phasor.PhaseDeg() << ") [" << phasor.Real() + << " + j*" << phasor.Imag() << "]" << std::endl + ); +} + +Phasor operator+(const Phasor& lhs, const Phasor& rhs) { + return Phasor(lhs.Real() + rhs.Real(), lhs.Imag() + rhs.Imag(), AlgTag {}); +} + +Phasor operator-(const Phasor& lhs, const Phasor& rhs) { + return Phasor(lhs.Real() - rhs.Real(), lhs.Imag() - rhs.Imag(), AlgTag {}); +} + +Phasor operator*(const Phasor& lhs, const Phasor& rhs) { + return Phasor(lhs.Abs() * rhs.Abs(), lhs.Phase() + rhs.Phase()); +} + +Phasor operator/(const Phasor& lhs, const Phasor& rhs) { + return Phasor(lhs.Abs() / rhs.Abs(), lhs.Phase() - rhs.Phase()); +} + +Phasor MakePhasorCartesian(const double x, const double y) { + return Phasor(x, y, AlgTag {}); +} + +Phasor MakePhasorPolar(const double m, const double phi) { + return Phasor(m, phi, ExpTag {}); +} + +Phasor MakePhasorPolarDeg(const double m, const double deg) { + return Phasor(m, deg, DegTag {}); +} \ No newline at end of file diff --git a/04_week/tasks/queue/queue.cpp b/04_week/tasks/queue/queue.cpp index 2a9f8493..e4648e33 100644 --- a/04_week/tasks/queue/queue.cpp +++ b/04_week/tasks/queue/queue.cpp @@ -1,6 +1,148 @@ #include +#include +#include class Queue { - +public: + Queue() = default; + Queue(std::stack stack); + Queue(std::vector vector); + Queue(std::initializer_list values); + Queue(const int size); + void Push(const int x); + bool Pop(); + int& Back(); + int Back() const; + int& Front(); + int Front() const; + bool Empty() const; + size_t Size() const; + void Clear(); + void Swap(Queue& other); + bool operator==(const Queue& other) const; + bool operator!=(const Queue& other) const; +private: + int m_null_value = 0; + std::vector m_input{}; + std::vector m_output{}; + std::vector m_merge_vectors(const auto& input, const auto& output) const; }; + +Queue::Queue(std::stack stack) { + while (!stack.empty()) { + m_output.push_back(stack.top()); + stack.pop(); + } +} + +Queue::Queue(std::vector vector) { + while (!vector.empty()) { + m_output.push_back(vector.back()); + vector.pop_back(); + } +} + +Queue::Queue(std::initializer_list ilist) { + for (const int v : ilist) { + m_input.push_back(v); + } +} + +Queue::Queue(const int size) { + m_input.reserve(size); + m_output.reserve(size); +} + +void Queue::Push(const int x) { + m_input.push_back(x); +} + +bool Queue::Pop() { + if (Empty()) { + return false; + } + + if (m_output.empty()) { + while (!m_input.empty()) { + m_output.push_back(m_input.back()); + m_input.pop_back(); + } + } + + m_output.pop_back(); + + return true; +} + +int& Queue::Back() { + if (Empty()) + return m_null_value; + + return m_input.empty() ? m_output.front() : m_input.back(); +} + +int Queue::Back() const { + if (Empty()) + return m_null_value; + + return m_input.empty() ? m_output.front() : m_input.back(); +} + +int& Queue::Front() { + if (Empty()) + return m_null_value; + + return m_output.empty() ? m_input.front() : m_output.back(); +} + +int Queue::Front() const { + if (Empty()) + return m_null_value; + + return m_output.empty() ? m_input.front() : m_output.back(); +} + +bool Queue::Empty() const { + return m_input.empty() && m_output.empty(); +} + +size_t Queue::Size() const { + return m_input.size() + m_output.size(); +} + +void Queue::Clear() { + m_input.clear(); + m_output.clear(); +} + +void Queue::Swap(Queue& other) { + m_input.swap(other.m_input); + m_output.swap(other.m_output); +} + +std::vector Queue::m_merge_vectors(const auto& input, const auto& output) const { + std::vector merged{}; + merged.reserve(input.size() + output.size()); + + for (int i = output.size() - 1; i >= 0; --i) { + merged.push_back(output[i]); + } + + for (size_t i = 0; i < input.size(); ++i) { + merged.push_back(input[i]); + } + + return merged; +} + +bool Queue::operator==(const Queue& other) const { + std::vector this_merged = m_merge_vectors(m_input, m_output); + std::vector other_merged = m_merge_vectors(other.m_input, other.m_output); + + return this_merged == other_merged; +} + +bool Queue::operator!=(const Queue& other) const { + return !(*this == other); +} \ No newline at end of file diff --git a/04_week/tasks/ring_buffer/ring_buffer.cpp b/04_week/tasks/ring_buffer/ring_buffer.cpp index e2b57ba2..22c5ab06 100644 --- a/04_week/tasks/ring_buffer/ring_buffer.cpp +++ b/04_week/tasks/ring_buffer/ring_buffer.cpp @@ -1,6 +1,256 @@ #include +#include +#include class RingBuffer { - +public: + RingBuffer(const size_t size); + RingBuffer(const size_t size, const int val); + RingBuffer(const std::initializer_list ilist); + RingBuffer(const RingBuffer& other); + void Push(const int item); + bool TryPush(const int item); + void Pop(); + bool TryPop(int& pop_value); + int& Front(); + int Front() const; + int& Back(); + int Back() const; + bool Empty() const; + bool Full() const; + size_t Size() const; + size_t Capacity() const; + void Clear(); + void Resize(const size_t size); + std::vector Vector() const; + int& operator[](const size_t idx); + int operator[](const size_t idx) const; + RingBuffer& operator=(const RingBuffer& other); +private: + int m_null_value = 0; + size_t m_begin = 0; + size_t m_end = 0; + size_t m_size = 0; + std::vector m_buffer{}; + size_t m_check_zero_size(const size_t size) const; + void m_pop_core(); + void m_push_back_core(const int item); + void m_copy_core(const RingBuffer& other); + int m_last() const; }; + +size_t RingBuffer::m_check_zero_size(const size_t size) const { + return (size == 0) ? 1 : size; +} + +int RingBuffer::m_last() const { + return (m_end == 0) ? Capacity() - 1 : m_end - 1; +} + +RingBuffer::RingBuffer(size_t size) { + m_buffer.reserve(m_check_zero_size(size)); +} + +RingBuffer::RingBuffer(const size_t size, const int val) { + m_buffer.reserve(m_check_zero_size(size)); + for (size_t i = 0; i < Capacity(); ++i) { + m_buffer.push_back(val); + } + m_size = m_buffer.size(); + m_end = m_buffer.size(); +} + +RingBuffer::RingBuffer(const std::initializer_list ilist) : m_buffer(ilist) { + if (m_buffer.empty()) { + m_buffer.reserve(1); + } + m_size = m_buffer.size(); + m_end = m_buffer.size(); +} + +void RingBuffer::m_copy_core(const RingBuffer& other) { + m_begin = other.m_begin; + m_end = other.m_end; + m_size = other.m_size; + m_buffer = other.m_buffer; + m_buffer.reserve(other.Capacity()); +} + +RingBuffer::RingBuffer(const RingBuffer& other) { + m_copy_core(other); +} + +inline void RingBuffer::m_push_back_core(const int item) { + if (m_buffer.size() == Capacity()) { + m_buffer[m_end] = item; + if (m_size != m_buffer.size()) { + ++m_size; + } + else { + m_begin = (m_begin + 1) % Capacity(); + } + } + else { + m_buffer.push_back(item); + ++m_size; + } + m_end = (m_end + 1) % Capacity(); +} + +void RingBuffer::Push(const int item) { + m_push_back_core(item); +} + +bool RingBuffer::TryPush(const int item) { + if (Full()) { + return false; + } + else { + m_push_back_core(item); + return true; + } +} + +inline void RingBuffer::m_pop_core() { + m_begin = (m_begin + 1) % Capacity(); + --m_size; +} + +void RingBuffer::Pop() { + if (Size() == 0) + return; + + m_pop_core(); +} + +bool RingBuffer::TryPop(int& pop_value) { + if (Size() == 0) + return false; + + pop_value = m_buffer[m_begin]; + + m_pop_core(); + + return true; +} + +int& RingBuffer::Front() { + if (Size() == 0) + return m_null_value; + + return m_buffer[m_last()]; +} + +int RingBuffer::Front() const { + if (Size() == 0) + return m_null_value; + + return m_buffer[m_last()]; +} + +int& RingBuffer::Back() { + if (Size() == 0) + return m_null_value; + + return m_buffer[m_begin]; +} + +int RingBuffer::Back() const { + if (Size() == 0) + return m_null_value; + + return m_buffer[m_begin]; +} + +bool RingBuffer::Empty() const { + return Size() == 0; +} + +bool RingBuffer::Full() const { + return Capacity() == Size(); +} + +size_t RingBuffer::Size() const { + return m_size; +} + +size_t RingBuffer::Capacity() const { + return m_buffer.capacity(); +} + +void RingBuffer::Clear() { + m_buffer.clear(); + m_size = 0; +} + +std::vector RingBuffer::Vector() const { + std::vector vector_buffer; + vector_buffer.reserve(Size()); + + if (m_end <= m_begin) { + size_t left_size = m_end; + size_t right_size = Size() - m_begin; + + size_t i = m_begin; + + while (i - m_begin < right_size) { + vector_buffer.push_back(m_buffer[i++]); + } + + i = 0; + while (i < left_size) { + vector_buffer.push_back(m_buffer[i++]); + } + } + else { + for (size_t i = 0; i < Size(); ++i) { + vector_buffer.push_back(m_buffer[i]); + } + } + + return vector_buffer; +} + +void RingBuffer::Resize(const size_t size) { + size_t new_size = m_check_zero_size(size); + + if (new_size == Capacity()) { + return; + } + + std::vector new_buffer; + new_buffer.reserve(new_size); + + std::vector vector_buffer = Vector(); + + for ( + size_t i = (Size() > new_size) ? Size() - new_size : 0; + new_buffer.size() != new_buffer.capacity() && i < Size(); + ++i + ) { + new_buffer.push_back(vector_buffer[i]); + } + + m_buffer = std::move(new_buffer); + m_begin = 0; + m_size = m_buffer.size(); + m_end = Size(); +} + +int& RingBuffer::operator[](const size_t idx) { + return m_buffer[(m_begin + idx) % Capacity()]; +} + +int RingBuffer::operator[](const size_t idx) const { + return m_buffer[(m_begin + idx) % Capacity()]; +} + +RingBuffer& RingBuffer::operator=(const RingBuffer& other) { + if (this == &other) { + return *this; + } + + m_copy_core(other); + return *this; +} diff --git a/04_week/tasks/stack/stack.cpp b/04_week/tasks/stack/stack.cpp index 222e4ffc..4a91ce60 100644 --- a/04_week/tasks/stack/stack.cpp +++ b/04_week/tasks/stack/stack.cpp @@ -1,6 +1,73 @@ #include +#include class Stack { - +public: + void Push(const int x); + bool Pop(); + int& Top(); + int Top() const; + bool Empty() const; + size_t Size() const; + void Clear(); + void Swap(Stack& other); + bool operator==(const Stack& other) const; + bool operator!=(const Stack& other) const; +private: + int m_null_value = 0; + std::vector m_stack{}; }; + +void Stack::Push(const int x) { + m_stack.push_back(x); +} + +bool Stack::Pop() { + if (Empty()) { + return false; + } + + m_stack.pop_back(); + return true; +} + +int Stack::Top() const { + if (Empty()) { + return m_null_value; + } + + return m_stack.back(); +} + +int& Stack::Top() { + if (Empty()) { + return m_null_value; + } + + return m_stack.back(); +} + +bool Stack::Empty() const { + return m_stack.empty(); +} + +size_t Stack::Size() const { + return m_stack.size(); +} + +void Stack::Clear() { + return m_stack.clear(); +} + +void Stack::Swap(Stack& other) { + m_stack.swap(other.m_stack); +} + +bool Stack::operator==(const Stack& other) const { + return m_stack == other.m_stack; +} + +bool Stack::operator!=(const Stack& other) const { + return m_stack != other.m_stack; +}