// // main.cpp // base // // Created by Andrew Owens on 2019-11-04. // Copyright (c) 2019 Andrew Owens. All rights reserved. // // NOTES: // To compile, you require (at least) c++ 11. // // Example: // g++ -std=c++11 main.cpp // ./a.out // #include // std::cout #include // std::unique_ptr #include // std::string // Base class // https://en.cppreference.com/w/cpp/language/derived_class class Speaker { public: virtual ~Speaker() = default; // required: otherwise, the destructor of // derived classes (e.g., Dog) is not called // when the object is destructed // interface defined by Speaker // derived classes must implement function definition(s) virtual void saySomething(std::string const &s) const = 0; }; //-------------------------- // Inheritance: Version 1 //-------------------------- // Dog inherits from Speak, and must give a function defintion to // 'saySomething()'. // Dog is now coupled to the interface Speak forevermore, which a bit // intrusive, and restrictive for reuse of the Dog class // By inheriting from Speaker, we are stating: // A dog "is-a" speaker class Dog : public Speaker { public: Dog(std::string name) : name(name) {} // Override virtual function from Base class. // The 'override' keyword is optional. It adds a compiler check that ensures // the declared function does in fact override a virtual function, which // catches errors like typos in the function name. // https://en.cppreference.com/w/cpp/language/override void saySomething(std::string const &s) const override { std::cout << name << " barks: " << s << '\n'; } std::string name; }; // Inheritance: Version 2 // Stand alone class class Cat { public: Cat(std::string name) : name(name) {} std::string name; }; // Free function that works with Cat // Benifits of separating the function from the virtual call: // -allows us to debug/test this function in isolation (outside of inheritence) // -this function is now reusable in a larger library (outside of inheritence) // -in the case where we are using a 3rd party libary, this may be the only way // to use their functions in our system inheritence hierarchy void catSays(Cat const &cat, std::string const &s) { std::cout << cat.name << " meows: " << s << '\n'; } // Wrapper class, derived from Speaker. // This avoids polluting Cat with inheritance and coupling it to // the interface of base class Speaker. class SpeakingCat : public Speaker { public: SpeakingCat(Cat cat) : talkingCat(cat) {} void saySomething(std::string const &s) const override { catSays(talkingCat, s); } Cat talkingCat; }; void pointersExample() { std::cout << "\n\n---pointersExample()---"; // ------------ // RAW POINTERS // ------------ std::cout << "\n-raw pointers-\n\n"; // raw pointer to Dog, 'Odie' // Odie's memory is dynamically allocated (on the heap). // p_raw is a local variable (inside the stack frame of the function). Speaker *p_raw = new Dog("Odie"); // polymorphic call Dog function 'saySometing' through Speak * s pointer. p_raw->saySomething("woof"); // Odie barks: woof // s_raw = new Dog("Max"); // LEAKED Memory: If we reassign the pointer, // there will be no way to know what address must be deleted to clean up // Odie's memory. Odie will remain on the heap, taking up memory until the // program ends. // delete memory on heap first delete p_raw; // raw pointer to Dog, 'Max' p_raw = new Dog("Max"); p_raw->saySomething("bow, wow"); // Max barks: bow, wow delete p_raw; // always clean up raw pointers // -------------- // SMART POINTERS // -------------- // https://en.cppreference.com/w/cpp/memory/unique_ptr std::cout << "\n-smart pointers-\n\n"; std::unique_ptr p_smart = std::unique_ptr(new Dog("Odie")); // In c++14 and above you can call 'make_unique' which is less // verbose. The template type is Dog, and the arguments are those you give the // constructor. // std::unique_ptr p_smart = std::make_unique("Odie"); p_smart->saySomething("woof, woof"); // Odie barks: woof, woof // reassigning smart pointer releases Odie's allocated memory p_smart = std::unique_ptr(new Dog("Max")); p_smart->saySomething("bow, wow WOW!"); // Max barks: bow, wow WOW! // no clean up required, automatically destructed } void polymorphismExample() { std::cout << "\n\n---polymorphismExample()---\n\n"; // Pointer of type Speaker... std::unique_ptr p_smart; //... points to an object of type Dog (allocated on the heap). p_smart = std::unique_ptr(new Dog("Odie")); p_smart->saySomething("woof, woof, woof"); // Odie barks: woof, woof, woof Cat garfield("Garfield"); // Now the pointer is reassigned to object of type Cat (allocated on the // heap). p_smart = std::unique_ptr(new SpeakingCat(garfield)); p_smart->saySomething("I hate Mondays."); // Garfield meows: I hate Mondays. } int main(int argc, const char *argv[]) { // Run my tests pointersExample(); polymorphismExample(); return 0; }