diff --git a/.gitignore b/.gitignore index 1f58f4e..b7df93d 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ compile_commands.json *.pdb *.log *.tmp +cpp_learning_lab_records.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index a94f696..5f651ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.20) project(cpp_learning_lab VERSION 0.1.0 LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -28,13 +28,13 @@ endfunction() function(cpp_lab_add_executable target_name) add_executable(${target_name} ${ARGN}) - target_compile_features(${target_name} PRIVATE cxx_std_20) + target_compile_features(${target_name} PRIVATE cxx_std_17) cpp_lab_apply_warnings(${target_name}) endfunction() function(cpp_lab_add_library target_name) add_library(${target_name} ${ARGN}) - target_compile_features(${target_name} PUBLIC cxx_std_20) + target_compile_features(${target_name} PUBLIC cxx_std_17) cpp_lab_apply_warnings(${target_name}) endfunction() @@ -45,16 +45,34 @@ cpp_lab_add_executable(en_01_types en/01_basics/types_and_initialization.cpp) cpp_lab_add_executable(en_02_references en/02_functions_references/references_const_correctness.cpp) cpp_lab_add_executable(en_03_class_invariants en/03_classes_oop/class_invariants.cpp) cpp_lab_add_executable(en_03_polymorphism en/03_classes_oop/polymorphism_virtual_destructor.cpp) +cpp_lab_add_executable(en_03_object_slicing en/03_classes_oop/object_slicing_warning.cpp) cpp_lab_add_executable(en_04_raii en/04_raii_memory/raii_unique_ptr.cpp) cpp_lab_add_executable(en_05_stl en/05_stl/vector_algorithm_basics.cpp) +cpp_lab_add_executable(en_06_smart_pointers en/06_smart_pointers/unique_shared_weak.cpp) +cpp_lab_add_executable(en_07_move_semantics en/07_move_semantics/rule_of_zero_and_move.cpp) +cpp_lab_add_executable(en_08_error_handling en/08_error_handling/optional_exceptions_status.cpp) +cpp_lab_add_executable(en_09_file_io en/09_file_io/file_stream_records.cpp) +cpp_lab_add_executable(en_10_templates en/10_templates/template_repository.cpp) +cpp_lab_add_executable(en_11_low_level en/11_low_level_cpp/bytes_alignment_flags.cpp) +cpp_lab_add_executable(en_12_concurrency en/12_concurrency/thread_safe_counter.cpp) +cpp_lab_add_executable(en_13_patterns en/13_patterns_architecture/strategy_pattern.cpp) cpp_lab_add_executable(tr_00_merhaba tr/00_toolchain/hello_modern_cpp.cpp) cpp_lab_add_executable(tr_01_tipler tr/01_basics/types_and_initialization.cpp) cpp_lab_add_executable(tr_02_referanslar tr/02_functions_references/references_const_correctness.cpp) cpp_lab_add_executable(tr_03_sinif_kurallari tr/03_classes_oop/class_invariants.cpp) cpp_lab_add_executable(tr_03_polimorfizm tr/03_classes_oop/polymorphism_virtual_destructor.cpp) +cpp_lab_add_executable(tr_03_object_slicing tr/03_classes_oop/object_slicing_warning.cpp) cpp_lab_add_executable(tr_04_raii tr/04_raii_memory/raii_unique_ptr.cpp) cpp_lab_add_executable(tr_05_stl tr/05_stl/vector_algorithm_basics.cpp) +cpp_lab_add_executable(tr_06_smart_pointerlar tr/06_smart_pointers/unique_shared_weak.cpp) +cpp_lab_add_executable(tr_07_move_semantics tr/07_move_semantics/rule_of_zero_and_move.cpp) +cpp_lab_add_executable(tr_08_hata_yonetimi tr/08_error_handling/optional_exceptions_status.cpp) +cpp_lab_add_executable(tr_09_file_io tr/09_file_io/file_stream_records.cpp) +cpp_lab_add_executable(tr_10_templates tr/10_templates/template_repository.cpp) +cpp_lab_add_executable(tr_11_low_level tr/11_low_level_cpp/bytes_alignment_flags.cpp) +cpp_lab_add_executable(tr_12_concurrency tr/12_concurrency/thread_safe_counter.cpp) +cpp_lab_add_executable(tr_13_patterns tr/13_patterns_architecture/strategy_pattern.cpp) add_subdirectory(en/projects/bank_account_oop) add_subdirectory(tr/projects/bank_account_oop) diff --git a/README.md b/README.md index e9f7ce6..4fef3c9 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,44 @@ # cpp-learning-lab Modern C++ Systems Foundation is a long-term C++ learning and reference lab. -The goal is not to collect random snippets. The goal is to build a repository -that explains C++ topics deeply enough to be useful later in interviews, -internships, embedded or systems work, and offline review. +The goal is not to collect random snippets. The goal is to build a practical +offline reference for interviews, internships, embedded or systems work, and +daily syntax recall. This repository is designed around three layers: -1. Language foundation: syntax, types, references, functions, classes, and OOP. -2. Engineering foundation: RAII, ownership, STL, tests, CMake, and CI. -3. Systems mindset: memory layout, performance, concurrency, low-level behavior, +1. Language foundation: types, functions, references, classes, and OOP. +2. Engineering foundation: RAII, ownership, STL, templates, tests, CMake, and CI. +3. Systems mindset: memory layout, file I/O, concurrency, low-level behavior, and production-style project structure. +## Standard Policy + +The repository uses **C++17 as the build baseline** because it is widely +available in production toolchains. C++20 and C++23 features are introduced as +modern notes where they matter, but the compiled examples stay portable and +industry-friendly. + ## Repository Structure ```txt . -├── docs/ # Build, style, debugging, and topic templates -├── en/ # English learning track -├── tr/ # Turkish learning track -├── CMakeLists.txt # Root build configuration -└── .github/workflows/ # CI build and test automation +|-- docs/ # Build, style, debugging, and topic templates +|-- en/ # English learning track +|-- tr/ # Turkish learning track +|-- CMakeLists.txt # Root build configuration +`-- .github/workflows/ # CI build and test automation ``` ## Current Foundation -- C++20-based build setup with CMake. +- C++17-based build setup with CMake. - Compiler warnings enabled for examples and projects. - GitHub Actions build and test workflow. -- English and Turkish learning paths. -- First examples for toolchain, types, references, OOP, RAII, and STL. +- English and Turkish learning paths and reference indexes. +- Examples for toolchain, types, references, OOP, RAII, smart pointers, move + semantics, STL, templates, error handling, file I/O, low-level C++, threading, + and architecture patterns. - A modular OOP bank account project with library, demo, and tests. ## How To Build @@ -55,9 +64,25 @@ ctest --test-dir build --output-on-failure - [English learning path](en/LEARNING_PATH.md) - [Turkish learning path](tr/LEARNING_PATH.md) -## Direction +## Topic Map + +- `00_toolchain`: compiler, CMake, CI, and build habits. +- `01_basics`: types, initialization, `auto`, and basic standard library usage. +- `02_functions_references`: function design, references, `const`, and intent. +- `03_classes_oop`: class invariants, encapsulation, inheritance, polymorphism. +- `04_raii_memory`: RAII, lifetime, ownership, and resource cleanup. +- `05_stl`: standard containers and algorithms. +- `06_smart_pointers`: `unique_ptr`, `shared_ptr`, `weak_ptr`, and ownership. +- `07_move_semantics`: copy, move, Rule of 0, and Rule of 5 direction. +- `08_error_handling`: exceptions, `std::optional`, and status-style returns. +- `09_file_io`: stream-based text record I/O. +- `10_templates`: reusable type-safe generic code. +- `11_low_level_cpp`: bytes, alignment, flags, and systems mental model. +- `12_concurrency`: threads, mutexes, and safe shared state. +- `13_patterns_architecture`: small design patterns without overengineering. + +## Maintenance Rule -The next phases will deepen OOP, RAII, smart pointers, move semantics, STL, -templates, error handling, concurrency, and low-level C++ topics. Each serious -topic should include explanation, mistakes, real-world usage, exercises, and -working code. +Add new material only when it teaches a real C++ idea, compiles cleanly, and is +linked from the learning path. The repository should stay useful as a handbook, +not become a pile of disconnected examples. diff --git a/docs/build-guide.md b/docs/build-guide.md index e8434ec..5d6b899 100644 --- a/docs/build-guide.md +++ b/docs/build-guide.md @@ -5,7 +5,7 @@ built in one consistent way. ## Requirements -- A C++20-capable compiler: +- A C++17-capable compiler: - GCC 10+ - Clang 12+ - MSVC 19.29+ diff --git a/docs/cpp-standard-policy.md b/docs/cpp-standard-policy.md new file mode 100644 index 0000000..e185ea1 --- /dev/null +++ b/docs/cpp-standard-policy.md @@ -0,0 +1,32 @@ +# C++ Standard Policy + +This repository uses C++17 as the compiled baseline. + +## Why C++17? + +- It is widely supported by GCC, Clang, and MSVC. +- It is common in production codebases that cannot upgrade toolchains quickly. +- It contains important modern features such as structured bindings, + `std::optional`, `std::variant`, `std::string_view`, and improved constexpr. +- It is a stable foundation before introducing C++20 features such as concepts, + ranges, coroutines, and modules. + +## How C++20 Fits + +C++20 should be explained in notes and future optional examples, especially for: + +- concepts +- ranges +- `std::span` +- coroutines +- modules +- stronger compile-time programming + +The default build should stay C++17 unless a topic explicitly needs a newer +standard. + +## Practical Rule + +Learn modern C++ style, but keep examples compatible with realistic company +toolchains. That balance is useful for interviews, embedded work, and restricted +development environments. diff --git a/docs/repo-roadmap.md b/docs/repo-roadmap.md new file mode 100644 index 0000000..fded7ff --- /dev/null +++ b/docs/repo-roadmap.md @@ -0,0 +1,36 @@ +# Repository Roadmap + +The repository is organized as a compact handbook. Each topic has a README for +the mental model and one focused example that compiles in CI. + +## Stable Foundation + +- Toolchain and CMake. +- Basic types and initialization. +- Functions, references, and const-correctness. +- OOP, invariants, polymorphism, and virtual destructors. +- RAII, smart pointers, and move semantics. +- STL containers and algorithms. +- Templates and generic code. +- Error handling, file I/O, low-level C++, and concurrency. + +## Project Layer + +The `bank_account_oop` project is intentionally small but production-shaped: + +- public header in `include/` +- implementation in `src/` +- demo executable in `src/main.cpp` +- assert-based tests in `tests/` +- CMake integration + +Future projects should follow this structure instead of becoming single-file +scripts. + +## Done Criteria For New Topics + +- The topic has a README. +- The example compiles through root CMake. +- The topic is linked from `LEARNING_PATH.md`. +- The code teaches a real concept, not only syntax. +- Naming and formatting match the rest of the repository. diff --git a/en/03_classes_oop/README.md b/en/03_classes_oop/README.md index ef3ab66..0ce9c02 100644 --- a/en/03_classes_oop/README.md +++ b/en/03_classes_oop/README.md @@ -12,9 +12,12 @@ modeling ownership, and deciding when runtime polymorphism is worth the cost. - Prefer composition before inheritance. - Use a virtual destructor for polymorphic base classes. - Avoid object slicing when working with derived objects. +- Pass polymorphic objects by reference or pointer, not by value. ## Examples - `class_invariants.cpp`: constructor validation and private state. - `polymorphism_virtual_destructor.cpp`: abstract base class and virtual destructor. +- `object_slicing_warning.cpp`: why passing derived objects by base value loses + derived behavior. diff --git a/en/03_classes_oop/object_slicing_warning.cpp b/en/03_classes_oop/object_slicing_warning.cpp new file mode 100644 index 0000000..e8846dc --- /dev/null +++ b/en/03_classes_oop/object_slicing_warning.cpp @@ -0,0 +1,35 @@ +#include +#include + +class Notification { +public: + virtual ~Notification() = default; + + virtual std::string channel() const { + return "generic"; + } +}; + +class EmailNotification final : public Notification { +public: + std::string channel() const override { + return "email"; + } +}; + +void printByValue(Notification notification) { + std::cout << "by value: " << notification.channel() << '\n'; +} + +void printByReference(const Notification& notification) { + std::cout << "by reference: " << notification.channel() << '\n'; +} + +int main() { + EmailNotification email; + + printByValue(email); + printByReference(email); + + return 0; +} diff --git a/en/03_classes_oop/polymorphism_virtual_destructor.cpp b/en/03_classes_oop/polymorphism_virtual_destructor.cpp index e6f80c8..1b1dd41 100644 --- a/en/03_classes_oop/polymorphism_virtual_destructor.cpp +++ b/en/03_classes_oop/polymorphism_virtual_destructor.cpp @@ -1,7 +1,5 @@ -#include #include #include -#include #include class Shape { @@ -17,7 +15,8 @@ class Circle final : public Shape { explicit Circle(double radius) : radius_{radius} {} double area() const override { - return std::numbers::pi * radius_ * radius_; + constexpr double pi = 3.14159265358979323846; + return pi * radius_ * radius_; } void print() const override { diff --git a/en/06_smart_pointers/README.md b/en/06_smart_pointers/README.md new file mode 100644 index 0000000..a3c8fc9 --- /dev/null +++ b/en/06_smart_pointers/README.md @@ -0,0 +1,23 @@ +# Smart Pointers + +Smart pointers make ownership visible in the type system. They do not remove +the need to understand lifetime, but they make common lifetime mistakes harder. + +## Mental Model + +- `std::unique_ptr` means one owner. +- `std::shared_ptr` means shared ownership. +- `std::weak_ptr` observes a shared object without extending its lifetime. +- Raw pointers and references are usually non-owning views in modern C++. + +## Common Mistakes + +- Using `shared_ptr` everywhere because it feels convenient. +- Creating ownership cycles with `shared_ptr`. +- Returning raw owning pointers from factory functions. +- Keeping a raw pointer after the owner has destroyed the object. + +## Example + +- `unique_shared_weak.cpp`: compares single ownership, shared ownership, and + non-owning observation. diff --git a/en/06_smart_pointers/unique_shared_weak.cpp b/en/06_smart_pointers/unique_shared_weak.cpp new file mode 100644 index 0000000..7948f40 --- /dev/null +++ b/en/06_smart_pointers/unique_shared_weak.cpp @@ -0,0 +1,51 @@ +#include +#include +#include +#include + +class Sensor { +public: + Sensor(std::string name, int value) : name_{std::move(name)}, value_{value} {} + + const std::string& name() const noexcept { + return name_; + } + + int read() const noexcept { + return value_; + } + +private: + std::string name_; + int value_; +}; + +class Dashboard { +public: + explicit Dashboard(std::weak_ptr sensor) : sensor_{std::move(sensor)} {} + + void print() const { + if (const auto sensor = sensor_.lock()) { + std::cout << sensor->name() << ": " << sensor->read() << '\n'; + } else { + std::cout << "sensor is no longer available\n"; + } + } + +private: + std::weak_ptr sensor_; +}; + +int main() { + auto uniqueSensor = std::make_unique("temperature", 24); + std::cout << uniqueSensor->name() << ": " << uniqueSensor->read() << '\n'; + + auto sharedSensor = std::make_shared("pressure", 101); + Dashboard dashboard{sharedSensor}; + + dashboard.print(); + sharedSensor.reset(); + dashboard.print(); + + return 0; +} diff --git a/en/07_move_semantics/README.md b/en/07_move_semantics/README.md new file mode 100644 index 0000000..cd593f8 --- /dev/null +++ b/en/07_move_semantics/README.md @@ -0,0 +1,20 @@ +# Move Semantics + +Move semantics allow resources to be transferred instead of copied. They are +central to modern C++ performance and ownership design. + +## Mental Model + +- Copying duplicates a value. +- Moving transfers resources from one object to another. +- A moved-from object must remain valid, but its old value should not be relied + on unless the type documents it. +- Rule of 0: prefer standard library members so the compiler can generate copy, + move, and destruction behavior. +- Rule of 5: if a class manually owns a resource, think about destructor, copy + constructor, copy assignment, move constructor, and move assignment together. + +## Example + +- `rule_of_zero_and_move.cpp`: shows a resource-owning wrapper that can use the + Rule of 0 because `std::vector` manages memory. diff --git a/en/07_move_semantics/rule_of_zero_and_move.cpp b/en/07_move_semantics/rule_of_zero_and_move.cpp new file mode 100644 index 0000000..f269260 --- /dev/null +++ b/en/07_move_semantics/rule_of_zero_and_move.cpp @@ -0,0 +1,34 @@ +#include +#include +#include +#include + +class Buffer { +public: + explicit Buffer(std::vector data) : data_{std::move(data)} {} + + std::size_t size() const noexcept { + return data_.size(); + } + + int firstOrZero() const noexcept { + return data_.empty() ? 0 : data_.front(); + } + +private: + std::vector data_; +}; + +Buffer makeBuffer() { + return Buffer{std::vector{10, 20, 30, 40}}; +} + +int main() { + Buffer original = makeBuffer(); + Buffer moved = std::move(original); + + std::cout << "moved size: " << moved.size() << '\n'; + std::cout << "moved first value: " << moved.firstOrZero() << '\n'; + + return 0; +} diff --git a/en/08_error_handling/README.md b/en/08_error_handling/README.md new file mode 100644 index 0000000..9265843 --- /dev/null +++ b/en/08_error_handling/README.md @@ -0,0 +1,17 @@ +# Error Handling + +C++ code should make failure modes explicit. Not every failure needs an +exception, and not every status code is enough. + +## Mental Model + +- Use exceptions for construction failures and violations that prevent normal + execution. +- Use `std::optional` when a result may simply be absent. +- Use status enums when callers need to branch on known outcomes. +- Keep validation close to the boundary where data enters the system. + +## Example + +- `optional_exceptions_status.cpp`: combines `std::optional`, exceptions, and a + small status enum. diff --git a/en/08_error_handling/optional_exceptions_status.cpp b/en/08_error_handling/optional_exceptions_status.cpp new file mode 100644 index 0000000..9e30f6a --- /dev/null +++ b/en/08_error_handling/optional_exceptions_status.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include + +std::optional parsePort(const std::string& text) { + try { + const int value = std::stoi(text); + if (value < 1 || value > 65'535) { + return std::nullopt; + } + return value; + } catch (const std::exception&) { + return std::nullopt; + } +} + +class Config { +public: + explicit Config(int port) : port_{port} { + if (port_ < 1 || port_ > 65'535) { + throw std::invalid_argument{"port is outside the valid range"}; + } + } + + int port() const noexcept { + return port_; + } + +private: + int port_; +}; + +enum class SaveStatus { + ok, + validationFailed, +}; + +SaveStatus validateBeforeSave(const Config& config) noexcept { + return config.port() > 0 ? SaveStatus::ok : SaveStatus::validationFailed; +} + +int main() { + const auto parsed = parsePort("8080"); + if (!parsed.has_value()) { + std::cout << "invalid port\n"; + return 1; + } + + const Config config{*parsed}; + const SaveStatus status = validateBeforeSave(config); + + std::cout << "port: " << config.port() << '\n'; + std::cout << "save allowed: " << std::boolalpha << (status == SaveStatus::ok) << '\n'; + + return 0; +} diff --git a/en/09_file_io/README.md b/en/09_file_io/README.md new file mode 100644 index 0000000..bbd7ebd --- /dev/null +++ b/en/09_file_io/README.md @@ -0,0 +1,16 @@ +# File I/O + +File I/O is where C++ programs meet the outside world. Streams are safer than +manual C-style buffer handling for most learning and application-level tasks. + +## Mental Model + +- `std::ofstream` writes to files. +- `std::ifstream` reads from files. +- Always check whether opening the file succeeded. +- Store records in a simple format before inventing a parser. +- Keep parsing separate from business logic. + +## Example + +- `file_stream_records.cpp`: writes text records and reads them back. diff --git a/en/09_file_io/file_stream_records.cpp b/en/09_file_io/file_stream_records.cpp new file mode 100644 index 0000000..00e7daa --- /dev/null +++ b/en/09_file_io/file_stream_records.cpp @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include + +struct Record { + int id; + std::string name; +}; + +std::string serialize(const Record& record) { + return std::to_string(record.id) + "," + record.name; +} + +Record parseLine(const std::string& line) { + std::istringstream stream{line}; + std::string idText; + std::string name; + + std::getline(stream, idText, ','); + std::getline(stream, name); + + return Record{std::stoi(idText), name}; +} + +int main() { + const std::vector records{{1, "Ada"}, {2, "Bjarne"}, {3, "Grace"}}; + const std::string fileName{"cpp_learning_lab_records.txt"}; + + { + std::ofstream output{fileName}; + if (!output) { + std::cout << "could not open file for writing\n"; + return 1; + } + + for (const auto& record : records) { + output << serialize(record) << '\n'; + } + } + + std::ifstream input{fileName}; + if (!input) { + std::cout << "could not open file for reading\n"; + return 1; + } + + std::string line; + while (std::getline(input, line)) { + const Record record = parseLine(line); + std::cout << record.id << " -> " << record.name << '\n'; + } + + return 0; +} diff --git a/en/10_templates/README.md b/en/10_templates/README.md new file mode 100644 index 0000000..33a52d2 --- /dev/null +++ b/en/10_templates/README.md @@ -0,0 +1,16 @@ +# Templates + +Templates let code work with types known at compile time. They are powerful, but +they should be used to remove real duplication or express a type-safe pattern. + +## Mental Model + +- Function templates generate functions for the used types. +- Class templates generate classes for the used types. +- Templates are checked when instantiated. +- Clear names and small interfaces matter because template errors can be noisy. + +## Example + +- `template_repository.cpp`: a tiny generic repository with a predicate-based + lookup. diff --git a/en/10_templates/template_repository.cpp b/en/10_templates/template_repository.cpp new file mode 100644 index 0000000..255fdce --- /dev/null +++ b/en/10_templates/template_repository.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include + +template +class Repository { +public: + void add(T value) { + items_.push_back(std::move(value)); + } + + template + std::optional findFirst(Predicate predicate) const { + for (const auto& item : items_) { + if (predicate(item)) { + return item; + } + } + return std::nullopt; + } + +private: + std::vector items_; +}; + +struct Book { + int id; + std::string title; +}; + +int main() { + Repository books; + books.add(Book{1, "Effective C++"}); + books.add(Book{2, "A Tour of C++"}); + + const auto found = books.findFirst([](const Book& book) { + return book.id == 2; + }); + + if (found.has_value()) { + std::cout << found->title << '\n'; + } + + return 0; +} diff --git a/en/11_low_level_cpp/README.md b/en/11_low_level_cpp/README.md new file mode 100644 index 0000000..9e44c9f --- /dev/null +++ b/en/11_low_level_cpp/README.md @@ -0,0 +1,16 @@ +# Low-Level C++ + +C++ can model low-level data while still using safer abstractions. The important +part is knowing where bytes, alignment, flags, and object lifetime matter. + +## Mental Model + +- `sizeof(T)` tells how many bytes an object occupies. +- `alignof(T)` tells the required alignment. +- `std::byte` represents raw bytes without pretending they are characters. +- Bit flags compactly store multiple boolean states. +- Low-level code needs clear boundaries and tests. + +## Example + +- `bytes_alignment_flags.cpp`: shows byte values, alignment, and bit flags. diff --git a/en/11_low_level_cpp/bytes_alignment_flags.cpp b/en/11_low_level_cpp/bytes_alignment_flags.cpp new file mode 100644 index 0000000..206aca3 --- /dev/null +++ b/en/11_low_level_cpp/bytes_alignment_flags.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +struct PacketHeader { + std::uint16_t type; + std::uint16_t length; +}; + +constexpr unsigned readFlag = 1U << 0U; +constexpr unsigned writeFlag = 1U << 1U; +constexpr unsigned executeFlag = 1U << 2U; + +bool hasFlag(const unsigned flags, const unsigned flag) noexcept { + return (flags & flag) != 0U; +} + +int main() { + const PacketHeader header{7, 128}; + const unsigned permissions = readFlag | writeFlag; + const std::array bytes{ + std::byte{0xDE}, + std::byte{0xAD}, + std::byte{0xBE}, + std::byte{0xEF}, + }; + + std::cout << "header size: " << sizeof(header) << '\n'; + std::cout << "header alignment: " << alignof(PacketHeader) << '\n'; + std::cout << "can read: " << std::boolalpha << hasFlag(permissions, readFlag) << '\n'; + std::cout << "can execute: " << hasFlag(permissions, executeFlag) << '\n'; + std::cout << "first byte: " << std::to_integer(bytes.front()) << '\n'; + + return 0; +} diff --git a/en/12_concurrency/README.md b/en/12_concurrency/README.md new file mode 100644 index 0000000..a4603c4 --- /dev/null +++ b/en/12_concurrency/README.md @@ -0,0 +1,16 @@ +# Concurrency + +Concurrency lets multiple tasks make progress, but shared state must be +protected. Correctness matters more than clever threading. + +## Mental Model + +- `std::thread` starts work on another thread. +- `std::mutex` protects shared mutable state. +- `std::lock_guard` locks in a scope and unlocks automatically. +- Avoid sharing data unless there is a clear reason. + +## Example + +- `thread_safe_counter.cpp`: increments a shared counter safely from multiple + threads. diff --git a/en/12_concurrency/thread_safe_counter.cpp b/en/12_concurrency/thread_safe_counter.cpp new file mode 100644 index 0000000..17cf9dc --- /dev/null +++ b/en/12_concurrency/thread_safe_counter.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +class Counter { +public: + void increment() { + const std::lock_guard lock{mutex_}; + ++value_; + } + + int value() const { + const std::lock_guard lock{mutex_}; + return value_; + } + +private: + mutable std::mutex mutex_; + int value_{0}; +}; + +int main() { + Counter counter; + constexpr int threadCount = 4; + constexpr int incrementsPerThread = 1'000; + + std::vector workers; + workers.reserve(static_cast(threadCount)); + + for (int i = 0; i < threadCount; ++i) { + workers.emplace_back([&counter]() { + for (int step = 0; step < incrementsPerThread; ++step) { + counter.increment(); + } + }); + } + + for (auto& worker : workers) { + worker.join(); + } + + std::cout << "counter: " << counter.value() << '\n'; + return 0; +} diff --git a/en/13_patterns_architecture/README.md b/en/13_patterns_architecture/README.md new file mode 100644 index 0000000..7831489 --- /dev/null +++ b/en/13_patterns_architecture/README.md @@ -0,0 +1,17 @@ +# Patterns And Architecture + +Patterns are useful when they name a real design pressure. They become harmful +when they are added before the problem exists. + +## Mental Model + +- Prefer simple functions and composition first. +- Use Strategy when behavior must be interchangeable. +- Use Factory when object creation has enough variation to hide behind a + function. +- Keep interfaces small and stable. + +## Example + +- `strategy_pattern.cpp`: uses a pricing strategy without hardcoding one + discount rule into checkout logic. diff --git a/en/13_patterns_architecture/strategy_pattern.cpp b/en/13_patterns_architecture/strategy_pattern.cpp new file mode 100644 index 0000000..74cbb4c --- /dev/null +++ b/en/13_patterns_architecture/strategy_pattern.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +class DiscountStrategy { +public: + virtual ~DiscountStrategy() = default; + virtual double apply(double price) const = 0; +}; + +class NoDiscount final : public DiscountStrategy { +public: + double apply(const double price) const override { + return price; + } +}; + +class StudentDiscount final : public DiscountStrategy { +public: + double apply(const double price) const override { + return price * 0.85; + } +}; + +class Checkout { +public: + explicit Checkout(std::unique_ptr strategy) + : strategy_{std::move(strategy)} {} + + double finalPrice(const double price) const { + return strategy_->apply(price); + } + +private: + std::unique_ptr strategy_; +}; + +int main() { + Checkout checkout{std::make_unique()}; + std::cout << "final price: " << checkout.finalPrice(200.0) << '\n'; + + return 0; +} diff --git a/en/LEARNING_PATH.md b/en/LEARNING_PATH.md index 280f6df..db85791 100644 --- a/en/LEARNING_PATH.md +++ b/en/LEARNING_PATH.md @@ -1,6 +1,7 @@ # English Learning Path -This path is designed to move from syntax to professional C++ habits. +This path moves from syntax to professional C++ habits without jumping into +advanced topics too early. ## Phase 1: Tooling And Language Basics @@ -19,23 +20,44 @@ This path is designed to move from syntax to professional C++ habits. 7. `03_classes_oop/README.md` 8. `03_classes_oop/class_invariants.cpp` 9. `03_classes_oop/polymorphism_virtual_destructor.cpp` -10. `projects/bank_account_oop/README.md` +10. `03_classes_oop/object_slicing_warning.cpp` +11. `projects/bank_account_oop/README.md` -## Phase 4: RAII And Ownership +## Phase 4: Ownership And Lifetime -11. `04_raii_memory/README.md` -12. `04_raii_memory/raii_unique_ptr.cpp` +12. `04_raii_memory/README.md` +13. `04_raii_memory/raii_unique_ptr.cpp` +14. `06_smart_pointers/README.md` +15. `06_smart_pointers/unique_shared_weak.cpp` +16. `07_move_semantics/README.md` +17. `07_move_semantics/rule_of_zero_and_move.cpp` -## Phase 5: STL Foundation +## Phase 5: Standard Library And Generic Code -13. `05_stl/README.md` -14. `05_stl/vector_algorithm_basics.cpp` +18. `05_stl/README.md` +19. `05_stl/vector_algorithm_basics.cpp` +20. `10_templates/README.md` +21. `10_templates/template_repository.cpp` -## Upcoming Phases +## Phase 6: Application Boundaries -- Deep OOP: inheritance, object slicing, interfaces, composition, operators. -- Resource management: Rule of 0, Rule of 3, Rule of 5, move semantics. -- Smart pointers: `unique_ptr`, `shared_ptr`, `weak_ptr`, ownership graphs. -- STL: iterators, algorithms, maps, unordered maps, spans, string views. -- Templates: function templates, class templates, specialization, concepts. -- Systems C++: memory layout, alignment, cache behavior, threading, atomics. +22. `08_error_handling/README.md` +23. `08_error_handling/optional_exceptions_status.cpp` +24. `09_file_io/README.md` +25. `09_file_io/file_stream_records.cpp` + +## Phase 7: Systems And Architecture + +26. `11_low_level_cpp/README.md` +27. `11_low_level_cpp/bytes_alignment_flags.cpp` +28. `12_concurrency/README.md` +29. `12_concurrency/thread_safe_counter.cpp` +30. `13_patterns_architecture/README.md` +31. `13_patterns_architecture/strategy_pattern.cpp` + +## Phase 8: Project Practice + +32. `projects/bank_account_oop/include/bank_account.h` +33. `projects/bank_account_oop/src/bank_account.cpp` +34. `projects/bank_account_oop/src/main.cpp` +35. `projects/bank_account_oop/tests/test_bank_account.cpp` diff --git a/en/README.md b/en/README.md index 092c0a3..5c09e3f 100644 --- a/en/README.md +++ b/en/README.md @@ -1,23 +1,35 @@ # English Track -This track explains C++ in English so the repository can also work as a -portfolio-friendly reference. The focus is modern C++ with systems awareness: -clear ownership, value semantics, RAII, STL, OOP design, and build discipline. +This track explains C++ in English so the repository can work as a +portfolio-friendly and offline reference. The focus is C++17 production habits +with C++20 awareness: clear ownership, value semantics, RAII, STL, OOP design, +and build discipline. -## Current Topics +## Topics -- `00_toolchain`: compiler, CMake, and first executable. -- `01_basics`: types, initialization, `auto`, and basic output. -- `02_functions_references`: functions, references, `const`, and intent. -- `03_classes_oop`: classes, invariants, polymorphism, and virtual destructors. -- `04_raii_memory`: RAII, ownership, and smart pointer direction. +- `00_toolchain`: compiler, CMake, CI, and first executable. +- `01_basics`: types, initialization, `auto`, and safer defaults. +- `02_functions_references`: functions, references, `const`, and mutation + intent. +- `03_classes_oop`: classes, invariants, encapsulation, polymorphism, and + virtual destructors. +- `04_raii_memory`: resource lifetime and ownership. - `05_stl`: standard containers and algorithms. -- `projects/bank_account_oop`: small modular OOP project with tests. +- `06_smart_pointers`: `unique_ptr`, `shared_ptr`, `weak_ptr`. +- `07_move_semantics`: copy, move, Rule of 0, and Rule of 5 direction. +- `08_error_handling`: exceptions, `std::optional`, and status enums. +- `09_file_io`: stream-based text file records. +- `10_templates`: reusable type-safe generic code. +- `11_low_level_cpp`: bytes, alignment, and flags. +- `12_concurrency`: threads, mutexes, and shared state. +- `13_patterns_architecture`: small design patterns and architecture decisions. +- `projects/bank_account_oop`: modular OOP project with tests. ## How To Study -1. Read the topic README. -2. Compile and run the examples through the root CMake build. -3. Read the code slowly and trace object lifetime. -4. Modify one behavior and rebuild. -5. Add a small exercise or test when the concept becomes clear. +1. Follow `LEARNING_PATH.md`. +2. Read the topic README before the example. +3. Build everything from the repository root. +4. Trace object lifetime, ownership, and invariants. +5. Modify one behavior and rebuild. +6. Add new examples only when they teach a real concept. diff --git a/en/REFERENCE_INDEX.md b/en/REFERENCE_INDEX.md new file mode 100644 index 0000000..4bb6d15 --- /dev/null +++ b/en/REFERENCE_INDEX.md @@ -0,0 +1,21 @@ +# Reference Index + +Use this file when you remember the problem but not the exact topic name. + +| Need | Go To | +| --- | --- | +| Build and run the repo | `../docs/build-guide.md` | +| Choose C++17 vs C++20 | `../docs/cpp-standard-policy.md` | +| Understand class invariants | `03_classes_oop/README.md` | +| Review virtual destructors | `03_classes_oop/polymorphism_virtual_destructor.cpp` | +| Understand object slicing | `03_classes_oop/object_slicing_warning.cpp` | +| Review RAII | `04_raii_memory/README.md` | +| Pick a smart pointer | `06_smart_pointers/README.md` | +| Understand move semantics | `07_move_semantics/README.md` | +| Use `std::optional` | `08_error_handling/optional_exceptions_status.cpp` | +| Read and write simple files | `09_file_io/file_stream_records.cpp` | +| Write generic code | `10_templates/template_repository.cpp` | +| Review flags and bytes | `11_low_level_cpp/bytes_alignment_flags.cpp` | +| Protect shared state | `12_concurrency/thread_safe_counter.cpp` | +| Use a simple design pattern | `13_patterns_architecture/strategy_pattern.cpp` | +| Study modular OOP | `projects/bank_account_oop/README.md` | diff --git a/en/projects/bank_account_oop/README.md b/en/projects/bank_account_oop/README.md index a2e4596..a777c05 100644 --- a/en/projects/bank_account_oop/README.md +++ b/en/projects/bank_account_oop/README.md @@ -7,10 +7,10 @@ interface, implementation, demo code, and tests. ```txt bank_account_oop/ -├── include/bank_account.h -├── src/bank_account.cpp -├── src/main.cpp -└── tests/test_bank_account.cpp +|-- include/bank_account.h +|-- src/bank_account.cpp +|-- src/main.cpp +`-- tests/test_bank_account.cpp ``` ## Concepts diff --git a/en/projects/bank_account_oop/tests/test_bank_account.cpp b/en/projects/bank_account_oop/tests/test_bank_account.cpp index 562c3f2..30eea2b 100644 --- a/en/projects/bank_account_oop/tests/test_bank_account.cpp +++ b/en/projects/bank_account_oop/tests/test_bank_account.cpp @@ -1,14 +1,19 @@ #include "bank_account.h" -#include #include #include +void require(const bool condition) { + if (!condition) { + throw std::runtime_error{"test failed"}; + } +} + void testDepositIncreasesBalance() { BankAccount account{"Ada", 1'000}; account.deposit(500); - assert(account.balanceCents() == 1'500); + require(account.balanceCents() == 1'500); } void testWithdrawReturnsFalseWhenBalanceIsInsufficient() { @@ -16,8 +21,8 @@ void testWithdrawReturnsFalseWhenBalanceIsInsufficient() { const bool result = account.withdraw(2'000); - assert(!result); - assert(account.balanceCents() == 1'000); + require(!result); + require(account.balanceCents() == 1'000); } void testInvalidOwnerThrows() { @@ -30,11 +35,11 @@ void testInvalidOwnerThrows() { thrown = true; } - assert(thrown); + require(thrown); } void testFormatCentsPadsSmallKurusValues() { - assert(formatCents(1'005) == "10.05"); + require(formatCents(1'005) == "10.05"); } int main() { diff --git a/tr/03_classes_oop/README.md b/tr/03_classes_oop/README.md index 38c7ae9..e6bb0b9 100644 --- a/tr/03_classes_oop/README.md +++ b/tr/03_classes_oop/README.md @@ -13,9 +13,12 @@ verebilmektir. - Inheritance öncesi composition düşün. - Polymorphic base class için virtual destructor kullan. - Derived objectleri base value olarak kopyalarken object slicing riskini bil. +- Polymorphic nesneleri value olarak değil, reference veya pointer olarak geçir. ## Örnekler - `class_invariants.cpp`: constructor validation ve private state. - `polymorphism_virtual_destructor.cpp`: abstract base class ve virtual destructor. +- `object_slicing_warning.cpp`: derived nesneyi base value olarak geçirince + derived davranışın nasıl kaybolduğunu gösterir. diff --git a/tr/03_classes_oop/object_slicing_warning.cpp b/tr/03_classes_oop/object_slicing_warning.cpp new file mode 100644 index 0000000..fd62943 --- /dev/null +++ b/tr/03_classes_oop/object_slicing_warning.cpp @@ -0,0 +1,35 @@ +#include +#include + +class Notification { +public: + virtual ~Notification() = default; + + virtual std::string channel() const { + return "generic"; + } +}; + +class EmailNotification final : public Notification { +public: + std::string channel() const override { + return "email"; + } +}; + +void printByValue(Notification notification) { + std::cout << "value ile: " << notification.channel() << '\n'; +} + +void printByReference(const Notification& notification) { + std::cout << "reference ile: " << notification.channel() << '\n'; +} + +int main() { + EmailNotification email; + + printByValue(email); + printByReference(email); + + return 0; +} diff --git a/tr/03_classes_oop/polymorphism_virtual_destructor.cpp b/tr/03_classes_oop/polymorphism_virtual_destructor.cpp index 88c71d8..4bb843e 100644 --- a/tr/03_classes_oop/polymorphism_virtual_destructor.cpp +++ b/tr/03_classes_oop/polymorphism_virtual_destructor.cpp @@ -1,6 +1,5 @@ #include #include -#include #include class Shape { @@ -16,7 +15,8 @@ class Circle final : public Shape { explicit Circle(double radius) : radius_{radius} {} double area() const override { - return std::numbers::pi * radius_ * radius_; + constexpr double pi = 3.14159265358979323846; + return pi * radius_ * radius_; } void print() const override { diff --git a/tr/06_smart_pointers/README.md b/tr/06_smart_pointers/README.md new file mode 100644 index 0000000..4e15b1f --- /dev/null +++ b/tr/06_smart_pointers/README.md @@ -0,0 +1,23 @@ +# Smart Pointerlar + +Smart pointerlar ownership bilgisini tip sisteminde görünür hale getirir. +Lifetime bilgisini gereksiz yapmaz; ama sık yapılan ownership hatalarını azaltır. + +## Mental Model + +- `std::unique_ptr` tek sahip demektir. +- `std::shared_ptr` paylaşımlı ownership demektir. +- `std::weak_ptr` shared nesneyi sahip olmadan gözlemler. +- Modern C++ kodunda raw pointer ve reference çoğu zaman non-owning view'dir. + +## Yaygın Hatalar + +- Kolay geliyor diye her yerde `shared_ptr` kullanmak. +- `shared_ptr` ile ownership cycle oluşturmak. +- Factory fonksiyonundan owning raw pointer döndürmek. +- Owner nesne yok olduktan sonra raw pointer saklamak. + +## Örnek + +- `unique_shared_weak.cpp`: tek ownership, shared ownership ve non-owning + observation farkını gösterir. diff --git a/tr/06_smart_pointers/unique_shared_weak.cpp b/tr/06_smart_pointers/unique_shared_weak.cpp new file mode 100644 index 0000000..6b8213a --- /dev/null +++ b/tr/06_smart_pointers/unique_shared_weak.cpp @@ -0,0 +1,51 @@ +#include +#include +#include +#include + +class Sensor { +public: + Sensor(std::string name, int value) : name_{std::move(name)}, value_{value} {} + + const std::string& name() const noexcept { + return name_; + } + + int read() const noexcept { + return value_; + } + +private: + std::string name_; + int value_; +}; + +class Dashboard { +public: + explicit Dashboard(std::weak_ptr sensor) : sensor_{std::move(sensor)} {} + + void print() const { + if (const auto sensor = sensor_.lock()) { + std::cout << sensor->name() << ": " << sensor->read() << '\n'; + } else { + std::cout << "sensor artik mevcut degil\n"; + } + } + +private: + std::weak_ptr sensor_; +}; + +int main() { + auto uniqueSensor = std::make_unique("temperature", 24); + std::cout << uniqueSensor->name() << ": " << uniqueSensor->read() << '\n'; + + auto sharedSensor = std::make_shared("pressure", 101); + Dashboard dashboard{sharedSensor}; + + dashboard.print(); + sharedSensor.reset(); + dashboard.print(); + + return 0; +} diff --git a/tr/07_move_semantics/README.md b/tr/07_move_semantics/README.md new file mode 100644 index 0000000..efd2028 --- /dev/null +++ b/tr/07_move_semantics/README.md @@ -0,0 +1,20 @@ +# Move Semantics + +Move semantics, resource kopyalamak yerine resource sahipliğini başka bir +nesneye taşımayı sağlar. Modern C++ performansı ve ownership tasarımı için çok +kritiktir. + +## Mental Model + +- Copy, değeri çoğaltır. +- Move, resource'u başka nesneye taşır. +- Moved-from nesne geçerli kalmalıdır, ama eski değerine güvenilmemelidir. +- Rule of 0: mümkünse standard library üyeleri kullan, compiler özel memberları + kendisi üretsin. +- Rule of 5: class manuel resource yönetiyorsa destructor, copy constructor, + copy assignment, move constructor ve move assignment birlikte düşünülür. + +## Örnek + +- `rule_of_zero_and_move.cpp`: `std::vector` sayesinde Rule of 0 ile memory + yönetimi yapan küçük wrapper örneği. diff --git a/tr/07_move_semantics/rule_of_zero_and_move.cpp b/tr/07_move_semantics/rule_of_zero_and_move.cpp new file mode 100644 index 0000000..d44aa83 --- /dev/null +++ b/tr/07_move_semantics/rule_of_zero_and_move.cpp @@ -0,0 +1,34 @@ +#include +#include +#include +#include + +class Buffer { +public: + explicit Buffer(std::vector data) : data_{std::move(data)} {} + + std::size_t size() const noexcept { + return data_.size(); + } + + int firstOrZero() const noexcept { + return data_.empty() ? 0 : data_.front(); + } + +private: + std::vector data_; +}; + +Buffer makeBuffer() { + return Buffer{std::vector{10, 20, 30, 40}}; +} + +int main() { + Buffer original = makeBuffer(); + Buffer moved = std::move(original); + + std::cout << "tasınan boyut: " << moved.size() << '\n'; + std::cout << "tasınan ilk değer: " << moved.firstOrZero() << '\n'; + + return 0; +} diff --git a/tr/08_error_handling/README.md b/tr/08_error_handling/README.md new file mode 100644 index 0000000..811a536 --- /dev/null +++ b/tr/08_error_handling/README.md @@ -0,0 +1,17 @@ +# Hata Yönetimi + +C++ kodu hata ihtimallerini açık göstermelidir. Her hata exception istemez, +her status code da yeterli değildir. + +## Mental Model + +- Constructor başarısızlığı ve normal akışı sürdüremeyen durumlarda exception + kullanılabilir. +- Sonuç bazen yoksa `std::optional` uygundur. +- Çağıran taraf bilinen durumlara göre karar verecekse status enum kullanılır. +- Validation, verinin sisteme girdiği sınıra yakın yerde yapılmalıdır. + +## Örnek + +- `optional_exceptions_status.cpp`: `std::optional`, exception ve küçük status + enum kullanımını birlikte gösterir. diff --git a/tr/08_error_handling/optional_exceptions_status.cpp b/tr/08_error_handling/optional_exceptions_status.cpp new file mode 100644 index 0000000..fa28a5d --- /dev/null +++ b/tr/08_error_handling/optional_exceptions_status.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include + +std::optional parsePort(const std::string& text) { + try { + const int value = std::stoi(text); + if (value < 1 || value > 65'535) { + return std::nullopt; + } + return value; + } catch (const std::exception&) { + return std::nullopt; + } +} + +class Config { +public: + explicit Config(int port) : port_{port} { + if (port_ < 1 || port_ > 65'535) { + throw std::invalid_argument{"port geçerli aralıkta değil"}; + } + } + + int port() const noexcept { + return port_; + } + +private: + int port_; +}; + +enum class SaveStatus { + ok, + validationFailed, +}; + +SaveStatus validateBeforeSave(const Config& config) noexcept { + return config.port() > 0 ? SaveStatus::ok : SaveStatus::validationFailed; +} + +int main() { + const auto parsed = parsePort("8080"); + if (!parsed.has_value()) { + std::cout << "geçersiz port\n"; + return 1; + } + + const Config config{*parsed}; + const SaveStatus status = validateBeforeSave(config); + + std::cout << "port: " << config.port() << '\n'; + std::cout << "kaydetmeye uygun: " << std::boolalpha << (status == SaveStatus::ok) << '\n'; + + return 0; +} diff --git a/tr/09_file_io/README.md b/tr/09_file_io/README.md new file mode 100644 index 0000000..1f931c3 --- /dev/null +++ b/tr/09_file_io/README.md @@ -0,0 +1,17 @@ +# File I/O + +File I/O, C++ programının dış dünyayla veri alışverişi yaptığı yerdir. Stream +yapıları, çoğu öğrenme ve uygulama seviyesinde C-style buffer yönetiminden daha +güvenlidir. + +## Mental Model + +- `std::ofstream` dosyaya yazar. +- `std::ifstream` dosyadan okur. +- Dosya açma başarılı mı mutlaka kontrol edilir. +- Parser yazmadan önce basit ve okunabilir bir record formatı seçilir. +- Parsing logic, iş kuralından ayrı tutulmalıdır. + +## Örnek + +- `file_stream_records.cpp`: text record yazar ve geri okur. diff --git a/tr/09_file_io/file_stream_records.cpp b/tr/09_file_io/file_stream_records.cpp new file mode 100644 index 0000000..46a15b7 --- /dev/null +++ b/tr/09_file_io/file_stream_records.cpp @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include + +struct Record { + int id; + std::string name; +}; + +std::string serialize(const Record& record) { + return std::to_string(record.id) + "," + record.name; +} + +Record parseLine(const std::string& line) { + std::istringstream stream{line}; + std::string idText; + std::string name; + + std::getline(stream, idText, ','); + std::getline(stream, name); + + return Record{std::stoi(idText), name}; +} + +int main() { + const std::vector records{{1, "Ada"}, {2, "Bjarne"}, {3, "Grace"}}; + const std::string fileName{"cpp_learning_lab_records.txt"}; + + { + std::ofstream output{fileName}; + if (!output) { + std::cout << "dosya yazmak için açılamadı\n"; + return 1; + } + + for (const auto& record : records) { + output << serialize(record) << '\n'; + } + } + + std::ifstream input{fileName}; + if (!input) { + std::cout << "dosya okumak için açılamadı\n"; + return 1; + } + + std::string line; + while (std::getline(input, line)) { + const Record record = parseLine(line); + std::cout << record.id << " -> " << record.name << '\n'; + } + + return 0; +} diff --git a/tr/10_templates/README.md b/tr/10_templates/README.md new file mode 100644 index 0000000..2ace5d0 --- /dev/null +++ b/tr/10_templates/README.md @@ -0,0 +1,17 @@ +# Templates + +Templates, compile-time'da bilinen tiplerle tekrar kullanılabilir kod yazmayı +sağlar. Güçlüdür; ama gerçek duplication azaltmak veya type-safe pattern +kurmak için kullanılmalıdır. + +## Mental Model + +- Function template, kullanılan tiplere göre fonksiyon üretir. +- Class template, kullanılan tiplere göre class üretir. +- Template kodu instantiate edildiğinde kontrol edilir. +- Template hata mesajları uzun olabileceği için küçük interface ve açık isimler + önemlidir. + +## Örnek + +- `template_repository.cpp`: predicate ile arama yapan küçük generic repository. diff --git a/tr/10_templates/template_repository.cpp b/tr/10_templates/template_repository.cpp new file mode 100644 index 0000000..255fdce --- /dev/null +++ b/tr/10_templates/template_repository.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include + +template +class Repository { +public: + void add(T value) { + items_.push_back(std::move(value)); + } + + template + std::optional findFirst(Predicate predicate) const { + for (const auto& item : items_) { + if (predicate(item)) { + return item; + } + } + return std::nullopt; + } + +private: + std::vector items_; +}; + +struct Book { + int id; + std::string title; +}; + +int main() { + Repository books; + books.add(Book{1, "Effective C++"}); + books.add(Book{2, "A Tour of C++"}); + + const auto found = books.findFirst([](const Book& book) { + return book.id == 2; + }); + + if (found.has_value()) { + std::cout << found->title << '\n'; + } + + return 0; +} diff --git a/tr/11_low_level_cpp/README.md b/tr/11_low_level_cpp/README.md new file mode 100644 index 0000000..52dae7a --- /dev/null +++ b/tr/11_low_level_cpp/README.md @@ -0,0 +1,17 @@ +# Low-Level C++ + +C++ düşük seviye veriyi modelleyebilir ve bunu daha güvenli abstractionlarla +birlikte yapabilir. Önemli olan byte, alignment, flag ve object lifetime +konularının nerede kritik olduğunu bilmektir. + +## Mental Model + +- `sizeof(T)` nesnenin kaç byte yer kapladığını gösterir. +- `alignof(T)` nesnenin alignment ihtiyacını gösterir. +- `std::byte`, raw byte temsil eder ama karakter gibi davranmaz. +- Bit flag'ler birden fazla boolean durumu kompakt saklar. +- Low-level kodun sınırları net olmalı ve test edilmelidir. + +## Örnek + +- `bytes_alignment_flags.cpp`: byte değerleri, alignment ve bit flag gösterir. diff --git a/tr/11_low_level_cpp/bytes_alignment_flags.cpp b/tr/11_low_level_cpp/bytes_alignment_flags.cpp new file mode 100644 index 0000000..f646c8e --- /dev/null +++ b/tr/11_low_level_cpp/bytes_alignment_flags.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +struct PacketHeader { + std::uint16_t type; + std::uint16_t length; +}; + +constexpr unsigned readFlag = 1U << 0U; +constexpr unsigned writeFlag = 1U << 1U; +constexpr unsigned executeFlag = 1U << 2U; + +bool hasFlag(const unsigned flags, const unsigned flag) noexcept { + return (flags & flag) != 0U; +} + +int main() { + const PacketHeader header{7, 128}; + const unsigned permissions = readFlag | writeFlag; + const std::array bytes{ + std::byte{0xDE}, + std::byte{0xAD}, + std::byte{0xBE}, + std::byte{0xEF}, + }; + + std::cout << "header boyutu: " << sizeof(header) << '\n'; + std::cout << "header alignment: " << alignof(PacketHeader) << '\n'; + std::cout << "okuma izni: " << std::boolalpha << hasFlag(permissions, readFlag) << '\n'; + std::cout << "execute izni: " << hasFlag(permissions, executeFlag) << '\n'; + std::cout << "ilk byte: " << std::to_integer(bytes.front()) << '\n'; + + return 0; +} diff --git a/tr/12_concurrency/README.md b/tr/12_concurrency/README.md new file mode 100644 index 0000000..22e0254 --- /dev/null +++ b/tr/12_concurrency/README.md @@ -0,0 +1,16 @@ +# Concurrency + +Concurrency birden fazla işin ilerlemesini sağlar; ama shared state korunmak +zorundadır. Doğruluk, havalı threading kullanımından daha önemlidir. + +## Mental Model + +- `std::thread` başka thread üzerinde iş başlatır. +- `std::mutex` shared mutable state'i korur. +- `std::lock_guard` scope başında lock alır, scope sonunda otomatik bırakır. +- Veri paylaşma, gerçekten gerekmediği sürece azaltılmalıdır. + +## Örnek + +- `thread_safe_counter.cpp`: bir counter değerini birden fazla thread'den + güvenli artırır. diff --git a/tr/12_concurrency/thread_safe_counter.cpp b/tr/12_concurrency/thread_safe_counter.cpp new file mode 100644 index 0000000..17cf9dc --- /dev/null +++ b/tr/12_concurrency/thread_safe_counter.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +class Counter { +public: + void increment() { + const std::lock_guard lock{mutex_}; + ++value_; + } + + int value() const { + const std::lock_guard lock{mutex_}; + return value_; + } + +private: + mutable std::mutex mutex_; + int value_{0}; +}; + +int main() { + Counter counter; + constexpr int threadCount = 4; + constexpr int incrementsPerThread = 1'000; + + std::vector workers; + workers.reserve(static_cast(threadCount)); + + for (int i = 0; i < threadCount; ++i) { + workers.emplace_back([&counter]() { + for (int step = 0; step < incrementsPerThread; ++step) { + counter.increment(); + } + }); + } + + for (auto& worker : workers) { + worker.join(); + } + + std::cout << "counter: " << counter.value() << '\n'; + return 0; +} diff --git a/tr/13_patterns_architecture/README.md b/tr/13_patterns_architecture/README.md new file mode 100644 index 0000000..76cd1db --- /dev/null +++ b/tr/13_patterns_architecture/README.md @@ -0,0 +1,16 @@ +# Pattern Ve Architecture + +Pattern'lar gerçek bir tasarım baskısını isimlendirdiğinde faydalıdır. Problem +oluşmadan eklenirse kodu gereksiz karmaşıklaştırır. + +## Mental Model + +- Önce basit fonksiyon ve composition tercih edilir. +- Davranış değiştirilebilir olmalıysa Strategy kullanılabilir. +- Nesne oluşturma varyasyonları artıyorsa Factory düşünülür. +- Interface küçük ve stabil tutulmalıdır. + +## Örnek + +- `strategy_pattern.cpp`: checkout logic içine tek discount kuralı gömmek + yerine pricing strategy kullanır. diff --git a/tr/13_patterns_architecture/strategy_pattern.cpp b/tr/13_patterns_architecture/strategy_pattern.cpp new file mode 100644 index 0000000..0b4cce1 --- /dev/null +++ b/tr/13_patterns_architecture/strategy_pattern.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +class DiscountStrategy { +public: + virtual ~DiscountStrategy() = default; + virtual double apply(double price) const = 0; +}; + +class NoDiscount final : public DiscountStrategy { +public: + double apply(const double price) const override { + return price; + } +}; + +class StudentDiscount final : public DiscountStrategy { +public: + double apply(const double price) const override { + return price * 0.85; + } +}; + +class Checkout { +public: + explicit Checkout(std::unique_ptr strategy) + : strategy_{std::move(strategy)} {} + + double finalPrice(const double price) const { + return strategy_->apply(price); + } + +private: + std::unique_ptr strategy_; +}; + +int main() { + Checkout checkout{std::make_unique()}; + std::cout << "son fiyat: " << checkout.finalPrice(200.0) << '\n'; + + return 0; +} diff --git a/tr/LEARNING_PATH.md b/tr/LEARNING_PATH.md index a2e02e1..4e8061e 100644 --- a/tr/LEARNING_PATH.md +++ b/tr/LEARNING_PATH.md @@ -20,23 +20,44 @@ tasarlandı. 7. `03_classes_oop/README.md` 8. `03_classes_oop/class_invariants.cpp` 9. `03_classes_oop/polymorphism_virtual_destructor.cpp` -10. `projects/bank_account_oop/README.md` +10. `03_classes_oop/object_slicing_warning.cpp` +11. `projects/bank_account_oop/README.md` -## Faz 4: RAII Ve Ownership +## Faz 4: Ownership Ve Lifetime -11. `04_raii_memory/README.md` -12. `04_raii_memory/raii_unique_ptr.cpp` +12. `04_raii_memory/README.md` +13. `04_raii_memory/raii_unique_ptr.cpp` +14. `06_smart_pointers/README.md` +15. `06_smart_pointers/unique_shared_weak.cpp` +16. `07_move_semantics/README.md` +17. `07_move_semantics/rule_of_zero_and_move.cpp` -## Faz 5: STL Temeli +## Faz 5: Standard Library Ve Generic Code -13. `05_stl/README.md` -14. `05_stl/vector_algorithm_basics.cpp` +18. `05_stl/README.md` +19. `05_stl/vector_algorithm_basics.cpp` +20. `10_templates/README.md` +21. `10_templates/template_repository.cpp` -## Gelecek Fazlar +## Faz 6: Uygulama Sınırları -- Derin OOP: inheritance, object slicing, interface, composition, operatorler. -- Resource yönetimi: Rule of 0, Rule of 3, Rule of 5, move semantics. -- Smart pointerlar: `unique_ptr`, `shared_ptr`, `weak_ptr`, ownership graph. -- STL: iterator, algorithm, map, unordered map, span, string view. -- Template: function template, class template, specialization, concepts. -- Systems C++: memory layout, alignment, cache, threading, atomics. +22. `08_error_handling/README.md` +23. `08_error_handling/optional_exceptions_status.cpp` +24. `09_file_io/README.md` +25. `09_file_io/file_stream_records.cpp` + +## Faz 7: Systems Ve Architecture + +26. `11_low_level_cpp/README.md` +27. `11_low_level_cpp/bytes_alignment_flags.cpp` +28. `12_concurrency/README.md` +29. `12_concurrency/thread_safe_counter.cpp` +30. `13_patterns_architecture/README.md` +31. `13_patterns_architecture/strategy_pattern.cpp` + +## Faz 8: Proje Pratiği + +32. `projects/bank_account_oop/include/bank_account.h` +33. `projects/bank_account_oop/src/bank_account.cpp` +34. `projects/bank_account_oop/src/main.cpp` +35. `projects/bank_account_oop/tests/test_bank_account.cpp` diff --git a/tr/README.md b/tr/README.md index 78686c3..7a9cbf2 100644 --- a/tr/README.md +++ b/tr/README.md @@ -1,23 +1,34 @@ # Türkçe Yol Bu klasör C++ konularını Türkçe anlatır. Amaç sadece syntax ezberi yapmak -değil; OOP, RAII, ownership, STL, test, CMake ve sürdürülebilir proje düzenini -birlikte öğrenmektir. +değil; OOP, RAII, ownership, STL, template, test, CMake ve sürdürülebilir proje +düzenini birlikte öğrenmektir. -## Mevcut Konular +## Konular -- `00_toolchain`: derleyici, CMake ve ilk çalıştırılabilir dosya. -- `01_basics`: tipler, initialization, `auto` ve temel çıktı. -- `02_functions_references`: fonksiyonlar, referanslar, `const` ve niyet. -- `03_classes_oop`: class, invariant, polymorphism ve virtual destructor. -- `04_raii_memory`: RAII, ownership ve smart pointer yönü. -- `05_stl`: standart container ve algorithm temeli. -- `projects/bank_account_oop`: testli küçük modüler OOP projesi. +- `00_toolchain`: compiler, CMake, CI ve ilk çalıştırılabilir dosya. +- `01_basics`: tipler, initialization, `auto` ve güvenli başlangıçlar. +- `02_functions_references`: fonksiyonlar, referanslar, `const` ve mutation + niyeti. +- `03_classes_oop`: class, invariant, encapsulation, polymorphism ve virtual + destructor. +- `04_raii_memory`: resource lifetime ve ownership. +- `05_stl`: standard container ve algorithm temeli. +- `06_smart_pointers`: `unique_ptr`, `shared_ptr`, `weak_ptr`. +- `07_move_semantics`: copy, move, Rule of 0 ve Rule of 5 yönü. +- `08_error_handling`: exception, `std::optional` ve status enum. +- `09_file_io`: stream tabanlı text file record işlemleri. +- `10_templates`: type-safe generic code. +- `11_low_level_cpp`: byte, alignment ve flag mantığı. +- `12_concurrency`: thread, mutex ve shared state. +- `13_patterns_architecture`: küçük pattern ve architecture kararları. +- `projects/bank_account_oop`: testli modüler OOP projesi. ## Nasıl Çalışılır? -1. Önce konu README dosyasını oku. -2. Örneği root CMake build ile derle ve çalıştır. -3. Kodda object lifetime ve ownership akışını takip et. -4. Küçük bir davranışı değiştirip tekrar build al. -5. Konu netleşince küçük bir test veya egzersiz ekle. +1. `LEARNING_PATH.md` sırasını takip et. +2. Örnekten önce konu README dosyasını oku. +3. Her şeyi repo root klasöründen build et. +4. Object lifetime, ownership ve invariant akışını takip et. +5. Küçük bir davranışı değiştirip tekrar build al. +6. Yeni örnekleri sadece gerçek bir C++ fikri öğretiyorsa ekle. diff --git a/tr/REFERENCE_INDEX.md b/tr/REFERENCE_INDEX.md new file mode 100644 index 0000000..1dfc54a --- /dev/null +++ b/tr/REFERENCE_INDEX.md @@ -0,0 +1,21 @@ +# Referans İndeksi + +Problemi hatırlayıp konu adını hatırlamadığında buradan hızlıca git. + +| İhtiyaç | Git | +| --- | --- | +| Repoyu build etmek | `../docs/build-guide.md` | +| C++17 mi C++20 mi seçmek | `../docs/cpp-standard-policy.md` | +| Class invariant hatırlamak | `03_classes_oop/README.md` | +| Virtual destructor bakmak | `03_classes_oop/polymorphism_virtual_destructor.cpp` | +| Object slicing anlamak | `03_classes_oop/object_slicing_warning.cpp` | +| RAII hatırlamak | `04_raii_memory/README.md` | +| Smart pointer seçmek | `06_smart_pointers/README.md` | +| Move semantics anlamak | `07_move_semantics/README.md` | +| `std::optional` kullanmak | `08_error_handling/optional_exceptions_status.cpp` | +| Basit dosya okuyup yazmak | `09_file_io/file_stream_records.cpp` | +| Generic code yazmak | `10_templates/template_repository.cpp` | +| Flag ve byte mantığı | `11_low_level_cpp/bytes_alignment_flags.cpp` | +| Shared state korumak | `12_concurrency/thread_safe_counter.cpp` | +| Basit pattern görmek | `13_patterns_architecture/strategy_pattern.cpp` | +| Modüler OOP çalışmak | `projects/bank_account_oop/README.md` | diff --git a/tr/projects/bank_account_oop/README.md b/tr/projects/bank_account_oop/README.md index c3497b0..deb8b9f 100644 --- a/tr/projects/bank_account_oop/README.md +++ b/tr/projects/bank_account_oop/README.md @@ -7,10 +7,10 @@ kodu ve testleri ayrı dosyalara böler. ```txt bank_account_oop/ -├── include/bank_account.h -├── src/bank_account.cpp -├── src/main.cpp -└── tests/test_bank_account.cpp +|-- include/bank_account.h +|-- src/bank_account.cpp +|-- src/main.cpp +`-- tests/test_bank_account.cpp ``` ## Konular diff --git a/tr/projects/bank_account_oop/tests/test_bank_account.cpp b/tr/projects/bank_account_oop/tests/test_bank_account.cpp index 562c3f2..30eea2b 100644 --- a/tr/projects/bank_account_oop/tests/test_bank_account.cpp +++ b/tr/projects/bank_account_oop/tests/test_bank_account.cpp @@ -1,14 +1,19 @@ #include "bank_account.h" -#include #include #include +void require(const bool condition) { + if (!condition) { + throw std::runtime_error{"test failed"}; + } +} + void testDepositIncreasesBalance() { BankAccount account{"Ada", 1'000}; account.deposit(500); - assert(account.balanceCents() == 1'500); + require(account.balanceCents() == 1'500); } void testWithdrawReturnsFalseWhenBalanceIsInsufficient() { @@ -16,8 +21,8 @@ void testWithdrawReturnsFalseWhenBalanceIsInsufficient() { const bool result = account.withdraw(2'000); - assert(!result); - assert(account.balanceCents() == 1'000); + require(!result); + require(account.balanceCents() == 1'000); } void testInvalidOwnerThrows() { @@ -30,11 +35,11 @@ void testInvalidOwnerThrows() { thrown = true; } - assert(thrown); + require(thrown); } void testFormatCentsPadsSmallKurusValues() { - assert(formatCents(1'005) == "10.05"); + require(formatCents(1'005) == "10.05"); } int main() {