diff --git a/CMakeLists.txt b/CMakeLists.txt index 871e64f..e4d9feb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,6 +87,9 @@ set(APP_SOURCES "src/core/datatypes/CStruct.cpp" "src/core/datatypes/CUnion.cpp" "src/core/datatypes/TypeConVersions.cpp" + "src/core/datatypes/class/CConstructors.cpp" + "src/core/datatypes/class/CDestructors.cpp" + "src/patterns/structural/Adapter.cpp" ) # Test files diff --git a/docs/uml/patterns_structural_adapter.drawio.svg b/docs/uml/patterns_structural_adapter.drawio.svg new file mode 100644 index 0000000..3da6dc6 --- /dev/null +++ b/docs/uml/patterns_structural_adapter.drawio.svg @@ -0,0 +1,4 @@ + + + +Client+ clientCode(Target *target): typeUseTarget + request(): typeAdaptee + specificRequest(): typeExtendsAdapter- m_adaptee: Adaptee + request(): typeUserequest(){m_daptee.specificRequest();....}Newly AddedNo Change \ No newline at end of file diff --git a/src/core/basics/InitializeVariable.cpp b/src/core/basics/InitializeVariable.cpp index 6ff05fa..0b0dcc6 100644 --- a/src/core/basics/InitializeVariable.cpp +++ b/src/core/basics/InitializeVariable.cpp @@ -25,7 +25,7 @@ struct Foo cout << "Default constructor/ default init\n"; } - // explicit Foo(int) + // Foo(int) explicit Foo(int) { cout << "Constructor called with int / copy init\n"; diff --git a/src/core/datatypes/class/CConstructors.cpp b/src/core/datatypes/class/CConstructors.cpp new file mode 100644 index 0000000..f8c00cd --- /dev/null +++ b/src/core/datatypes/class/CConstructors.cpp @@ -0,0 +1,254 @@ +#include +using namespace std; + +// *1. Members initializer list constructor +namespace InitializerList +{ + class CConstructors + { + private: + int m_x, m_y, m_z; + + public: + CConstructors(int x, int y) : m_x(x), m_y(y) + { + cout << "Called CConstructors(int x, int y) : m_x(x), m_y(y) \n"; + // m_z{0}; // error, it already initialized, so only can do assigment + // m_z(0); // error, it already initialized, so only can do assigment + m_z = 0; + } + + // using brace init + CConstructors(int x, int y, int z) : m_x{x}, m_y{y}, m_z{z} + { + cout << "Called CConstructors(int x, int y, int z) : m_x{x}, m_y{y}, m_z{z} \n"; + } + + // default arguments : must always be the RIGHTMOST parameters + explicit CConstructors(int x = 1) + { + cout << "Called CConstructors(int x = 1) \n"; + m_x = x; + m_y = 0; + m_z = 0; + } + + // CConstructors() = default; error: + // ‘InitializerList::CConstructors::CConstructors(int)’ cannot be overloaded with + // ‘InitializerList::CConstructors::CConstructors(int x = 1)’ + + void print() const + { + cout << "m_x = " << m_x << ", m_y = " << m_y << ", m_z = " << m_z << "\n"; + } + }; + + void constructers() + { + cout << "\n--- InitializerList Constructer Examples ---\n"; + CConstructors obj; + CConstructors obj1 = CConstructors(1, 2); + obj1.print(); + + CConstructors obj2 = CConstructors(3, 4, 5); + obj2.print(); + } +} + +// *2. Default constructor: is a constructor that accepts no arguments. +namespace Default +{ + class UConstructors + { + public: + // User-defined default constructor without argument + UConstructors() + { + cout << "Called UConstructors() \n"; + } + }; + + class IConstructors + { + public: + // Implicit default constructor is generated by the compiler when the class has no user-declared constructors + }; + + class EConstructors + { + public: + // we already create the constructor ourselves + // EConstructors(int a) + explicit EConstructors(float a) // explicit -> [[maybe_unused]] EConstructors obj2 = 1; [ERROR] + + { + cout << "Called explicit EConstructors(int a) \n"; + } + + // Explicit default constructor : also want the compiler to generate the default constructor. + EConstructors() = default; + }; + + void constructers() + { + cout << "\n--- Default Constructer Examples ---\n"; + [[maybe_unused]] UConstructors obj1; + // [[maybe_unused]] UConstructors obj2(); // wrong, this is function declare + // FYI: + // void outer() + // { + // void helper(); + // helper(); // defined later in the same file + // } + + [[maybe_unused]] UConstructors obj3{}; + + [[maybe_unused]] IConstructors obj4; + [[maybe_unused]] IConstructors obj6{}; + + [[maybe_unused]] EConstructors obj7; + [[maybe_unused]] EConstructors obj9{}; + + [[maybe_unused]] EConstructors obj10(1.2); + [[maybe_unused]] EConstructors obj11{2}; + } +} + +// *3. Delegate constructor: allow to delegate initialization to another constructor +namespace Delegate +{ + class CConstructor + { + private: + int m_x; + int m_y; + + public: + CConstructor(int x, int y) : m_x{x}, m_y{y} + { + cout << "Called CConstructor(int x, int y) : m_x{x}, m_y{y} \n"; + } + + explicit CConstructor(int x) : CConstructor{x, 1} + { + cout << "Called CConstructor(int x):CConstructor(x,0) \n"; + } + + CConstructor() : CConstructor(0) + { + cout << "Called CConstructor() : CConstructor(0) \n"; + } + + void print() const + { + cout << "m_x = " << m_x << ", m_y = " << m_y << "\n"; + } + }; + + void constructors() + { + cout << "\n--- Delegate Constructer Examples ---\n"; + CConstructor obj1 = CConstructor(); + obj1.print(); + } +} + +// *4. Copy constructor: initialize an copy object with an existing object +namespace Copy +{ + class ICConstructor // C++ will create a public implicit copy constructor for us if we do not provide a one. + { + private: + int m_x; + int m_y; + + public: + ICConstructor(int x, int y) : m_x{x}, m_y{y} + { + cout << "Called ICConstructor(int x, int y) : m_x{x}, m_y{y} \n"; + } + + void print() const + { + cout << "m_x = " << m_x << ", m_y = " << m_y << "\n"; + } + }; + + class ECConstructor // explicitly define our own copy constructor + { + private: + int m_x; + int m_y; + + public: + ECConstructor(int x, int y) : m_x{x}, m_y{y} + { + cout << "Called ICConstructor(int x, int y) : m_x{x}, m_y{y} \n"; + } + + // using `default` keyword + // ECConstructor(const ECConstructor &ref) = default; + + ECConstructor(const ECConstructor &ref) : m_x{ref.m_x}, m_y{ref.m_x} + { + cout << "Called ECConstructor(const ECConstructor& ref) : m_x{ref.m_x}, m_y{ref.m_x} \n"; + } + + void print() const + { + cout << "m_x = " << m_x << ", m_y = " << m_y << "\n"; + } + }; + + class DECConstructor // Delete the copy constructor so no copies can be made + { + private: + int m_x; + int m_y; + + public: + DECConstructor(int x, int y) : m_x{x}, m_y{y} + { + cout << "Called DECConstructor(int x, int y) : m_x{x}, m_y{y} \n"; + } + + // using `delete` keyword + DECConstructor(const DECConstructor &fraction) = delete; + + void print() const + { + cout << "m_x = " << m_x << ", m_y = " << m_y << "\n"; + } + }; + + void constructors() + { + cout << "\n--- Copy Constructer Examples ---\n"; + ICConstructor obj1 = ICConstructor(1, 2); + ICConstructor obj2{obj1}; + obj1.print(); + obj2.print(); + + ECConstructor obj3 = ECConstructor(3, 4); + ECConstructor obj4{obj3}; + obj3.print(); + obj4.print(); + + DECConstructor obj5 = DECConstructor(5, 6); + // DECConstructor obj6{obj5}; error + obj5.print(); + } +} + +struct CConstructorsAutoRuner +{ + CConstructorsAutoRuner() + { + InitializerList::constructers(); + Default::constructers(); + Delegate::constructors(); + Copy::constructors(); + } +}; + +static CConstructorsAutoRuner instance; diff --git a/src/core/datatypes/class/CDestructors.cpp b/src/core/datatypes/class/CDestructors.cpp new file mode 100644 index 0000000..333925b --- /dev/null +++ b/src/core/datatypes/class/CDestructors.cpp @@ -0,0 +1,90 @@ +#include +using namespace std; + +// *1. Basic Destructor +namespace Basic +{ + class CDestructors + { + public: + CDestructors() + { + cout << "Called CDestructors() \n"; + } + + ~CDestructors() + { + cout << "Called ~CDestructors() \n"; + } + + // Using `default` keyword + // ~CDestructors() = default; + + // Using `delete` // I forbid this destructor + // ~CDestructors() = delete; + }; + + void destructers() + { + cout << "\n--- Basic Destructer Examples ---\n"; + { + CDestructors obj; + } + } +} + +// *2. Virtual Destructor +namespace Virtual +{ + class CDestructorsBase // final => cannot inherit + { + public: + CDestructorsBase() + { + cout << "Called CDestructorsBase() \n"; + } + + virtual ~CDestructorsBase() + { + cout << "Called ~CDestructorsBase() \n"; + } + + // Using `default` keyword + // ~CDestructorsBase() = default; + }; + + class CDestructorsDerived : public CDestructorsBase + { + public: + CDestructorsDerived() + { + cout << "Called CDestructorsDerived() \n"; + } + + ~CDestructorsDerived() override + { + cout << "Called ~CDestructorsDerived() \n"; + } + }; + + void destructers() + { + cout << "\n--- Virtual Destructer Examples ---\n"; + CDestructorsDerived *derived = {new CDestructorsDerived()}; + CDestructorsBase *base{derived}; + delete base; + // without virtual -> only call ~CDestructorsBase() + // with virtual -> call ~CDestructorsBase() && ~CDestructorsDerived() + } +} + +struct CDestructorsAutoRuner +{ + CDestructorsAutoRuner() + { + Basic::destructers(); + Virtual::destructers(); + } +}; + +static CDestructorsAutoRuner instance; diff --git a/src/patterns/structural/Adapter.cpp b/src/patterns/structural/Adapter.cpp new file mode 100644 index 0000000..9d72a94 --- /dev/null +++ b/src/patterns/structural/Adapter.cpp @@ -0,0 +1,170 @@ +#include + +// Adapters make legacy code work with modern classes. +namespace AdapterPattern +{ + /** + * The Adaptee contains some useful behavior, but its interface is incompatible + * with the existing client code. The Adaptee needs some adaptation before the + * client code can use it. + */ + class Adaptee + { + public: + static std::string specificRequest() + { + return "Adaptee: The adaptee's behavior."; + } + }; + + /** + * The Target defines the domain-specific interface used by the client code. + */ + class Target + { + public: + virtual std::string request() const + { + return "Target: The target's behavior."; + } + }; + + // ============================================================================================================ + // [Q] How can we make the clientCode works with Adaptee without change this function (e.g this is in front-end) + // => Create an Adapter + // ============================================================================================================ + + /** + * The Adapter makes the Adaptee's interface compatible with the Target's + * interface. + */ + class Adapter : public Target + { + private: + Adaptee *m_adaptee; + + public: + explicit Adapter(Adaptee *adaptee) : m_adaptee{adaptee} + { + std::cout << "Adapter constructer.\n"; + } + + std::string request() const override + { + return m_adaptee->specificRequest(); + } + }; + + /** + * The client code supports all classes that follow the Target interface. + */ + + namespace Client + { + void clientCode(const Target *target) + { + if (target != nullptr) + std::cout << "Output: " << target->request() << "\n"; + } + } + + void run() + { + std::cout << "Client: Can work just fine with the Target objects:\n"; + Target target = Target(); + std::cout << "Target: " << target.request() << "\n"; + Client::clientCode(&target); + std::cout << "\n\n"; + + std::cout << "Client: Cannot work with the Adaptee objects:\n"; + Adaptee adaptee = Adaptee(); + std::cout << "Adaptee: " << adaptee.specificRequest() << "\n"; + // Client::clientCode(&adaptee); // error + + std::cout << "Client: But can work with it via the Adapter:\n"; + Adapter adapter = Adapter(&adaptee); + Client::clientCode(&adapter); + std::cout << "\n"; + } +} + +namespace CaseStudy +{ + // Target interface expected by the existing system + class PaymentSystem + { + public: + virtual void payWithCard(const std::string &cardNumber) + { + std::cout << "Payment using card: " << cardNumber << "\n"; + } + + virtual ~PaymentSystem() = default; + }; + + // Adaptee: a new payment API with an incompatible interface + class PayPalAPI + { + public: + static void sendPayment(const std::string &email) + { + std::cout << "Payment sent via PayPal to " << email << "\n"; + } + }; + + // Adapter: makes PayPalAPI compatible with PaymentSystem + class PayPalAdapter : public PaymentSystem + { + private: + PayPalAPI paypal; + + public: + void payWithCard(const std::string &cardNumber) override + { + // Treat the cardNumber parameter as a PayPal email + paypal.sendPayment(cardNumber); + } + }; + + // Client code: uses the old interface without modification + void run() + { + std::string method, input; + method = std::string("card") + std::string(""); + input = "1234-5678-9999"; + // method = std::string("paypal") + std::string("");input = "user@example.com"; + + std::cout << "Choose payment method (card/paypal): " << method << "\n"; + + PaymentSystem *paymentSystem = nullptr; + + if (method == "card") + { + paymentSystem = new PaymentSystem(); + paymentSystem->payWithCard(input); + } + else if (method == "paypal") + { + paymentSystem = new PayPalAdapter(); + paymentSystem->payWithCard(input); + } + else + { + std::cout << "Unsupported payment method!\n"; + } + + delete paymentSystem; + } +} + +struct AdapterAutoRuner +{ + AdapterAutoRuner() + { + std::cout << "\n--- Factory Pattern Example ---\n"; + AdapterPattern::run(); + CaseStudy::run(); + } +}; + +static AdapterAutoRuner instance; \ No newline at end of file