<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Polymorphism :: CS3 course</title><link>https://robark.gitlab.io/poly/index.html</link><description>Chapter Polymorphism/Inheritance Dynamic binding, virtual methods, copy constructor, assignment operator, friendship of insertion operator</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Fri, 07 Jun 2024 13:24:03 -0700</lastBuildDate><atom:link href="https://robark.gitlab.io/poly/index.xml" rel="self" type="application/rss+xml"/><item><title>Polymorphism/Inheritance</title><link>https://robark.gitlab.io/poly/example/index.html</link><pubDate>Fri, 07 Jun 2024 13:24:03 -0700</pubDate><guid>https://robark.gitlab.io/poly/example/index.html</guid><description>###Polymorphism/Inheritance example
#include &lt;vector&gt; #include &lt;iostream&gt; using namespace std; /* Runtime Polymorphism example: Inheriting from a base class and overriding base class methods. Then calling these overridden methods through the base class pointers will invoke the proper sub class method during runtime based on the object the pointer references. C++ Rule of Three: If your class explicitly requires a copy constructor or an assignment operator or a destructor then it probably requires all three. */ class Animal { public: Animal(); Animal(const Animal&amp; x); //copy ctor Animal&amp; operator=(const Animal&amp; rhs); //assignment operator virtual void noise(); //virtual so we can overload in subclass int* agep; virtual ~Animal(); //destructor must be virtual and must exist since we are using polymorphism }; Animal::Animal(){ cout &lt;&lt; "Animal ctor" &lt;&lt; endl; agep = new int; //call new from ctor and delete from dtor } Animal::Animal(const Animal&amp; x){ //copy ctor //agep=x.agep; //line above happens by default if copy ctor does not exist. //Not good as shallow copy causes memory leak //AND calling delete twice which is undefined behavior //since pointer will be deleted both at the end of //foo and ALSO deleted at the end of main. cout&lt;&lt;"Animal copy ctor called"&lt;&lt;endl; agep =new int; //allocate new memory for copy *agep=*(x.agep); //deep copy } Animal&amp; Animal::operator=(const Animal&amp; rhs){ if (this==&amp;rhs) return *this; //check for self assignment (ex. a=a;) *agep=*(rhs.agep); //this pointer is implicit for agep return *this; } Animal::~Animal(){ cout&lt;&lt;"Animal dtor"&lt;&lt;endl; delete agep; //must delete in dtor if used new in ctor } void Animal::noise(){ cout&lt;&lt;"grr"&lt;&lt;endl; } //============================================================== class Dog : public Animal{ public: Dog (); //ctor Dog(const Dog&amp; ); //copy ctor Dog&amp; operator=(const Dog&amp; rhs); ~Dog (); //dtor is virtual whether we explicitly declare it to be or not //since base class dtor is virtual static int count; //static variables are class level. So only one copy exists for //all instances of Dog class. bool shots; void noise(); //noise is virtual because base class noise is virtual friend ostream&amp; operator&lt;&lt;(ostream&amp; o, const Dog&amp; x); //insertion operator &lt;&lt; is declared as a friend of the Dog class. //Friendship is given not taken. }; int Dog::count=0; //must initialize static members at file scope Dog::Dog():shots(false){ //Dog ctor //note initialization list syntax :var(default value), count++; //shots=false; //same as using initialization list above //If shots was const, must use initialization list cout&lt;&lt;"Dog ctor: There are now "&lt;&lt;count&lt;&lt; " dogs"&lt;&lt;endl; } Dog::~Dog(){ //dtor is virtual so Animal dtor will also be called count--; cout&lt;&lt;"Dog dtor: There are now "&lt;&lt;count&lt;&lt; " dogs"&lt;&lt;endl; } Dog::Dog(const Dog&amp; d):Animal(d){ //Dog copy ctor calls base class copy ctor first shots=d.shots; //simple copy not a pointer count++; cout&lt;&lt;"Copy Ctor of dog: There are now "&lt;&lt;count&lt;&lt; " dogs"&lt;&lt;endl; } Dog&amp; Dog::operator=(const Dog&amp; rhs){ //override assignment operator cout &lt;&lt; "Dog assignment" &lt;&lt; endl; Animal::operator=(rhs); // call Animal assignment operator function sending rhs by reference // no need to check self assignment since Animal checks self assignment shots=rhs.shots; return *this; //so multiple assignment will work eg a=b=c } void Dog::noise(){ cout&lt;&lt;"ruf ruf"&lt;&lt;endl; } //============================================================== void foo(Dog f){ //copy ctor called *(f.agep)=10; //changing f should NOT change d in main scope cout &lt;&lt; "printing dog f IN foo "&lt;&lt; f &lt;&lt;endl; } ostream&amp; operator&lt;&lt;(ostream&amp; o, const Dog&amp; x){ //cout&lt;&lt;A&lt;&lt;B; o &lt;&lt; "Age: " &lt;&lt; *(x.agep) &lt;&lt; " -- " &lt;&lt; "Shots: " &lt;&lt; x.shots; return o; } int main(){ Dog d; //stack *(d.agep)=5; d.shots=true; foo(d); //copy ctor called passing d to f in foo cout &lt;&lt;"printing dog AFTER foo "&lt;&lt; d &lt;&lt;endl; cout &lt;&lt; *(d.agep) &lt;&lt; " should still be 5 after foo if copy ctor works"&lt;&lt;endl; if (1) { Dog R; // Dog R only exists in this, if block scope R=d; //needs overloaded operator= cout&lt;&lt;R&lt;&lt;endl; //overloaded friend insertion operator } Animal* ap=&amp;d; ap-&gt;noise(); //polymorphism: //Base class Animal pointer ap points to Dog object, so Dog::noise() is called //we can now make a vector &lt;Animal*&gt; v and push_back dog, cat, bird pointers. //if we iterate through the vector and call -&gt;noise() we will get the correct //behavior ruf ruf, meow, tweet for all pointers. This is dynamic binding. return 0; } Output:</description></item></channel></rss>