210 likes | 327 Views
Class #19 – Copy constructors, Assignment Operators, Virtual Functions. Copy Constructors. A copy constructor is a special constructor that takes as its argument a reference to an object of the same class and creates a new object that is a copy.
E N D
Class #19 – Copy constructors, Assignment Operators, Virtual Functions
Copy Constructors • A copy constructor is a special constructor that takes as its argument a reference to an object of the same class and creates a new object that is a copy. • By default, the compiler provides a copy constructor that performs a member-by-member copy from the original object to the one being created. • This is called a member wise, or shallow, copy.
Copy Constructors • Although this might seem ok, in many cases a shallow copy is not what we want. • The problem is, any dynamic memory your class uses is stored by a pointer. • The pointer is copied, not the actual data that it’s pointing to. • So, you have two classes pointing to the same dynamic memory (linked list, etc.). So any change to one effects both. If your class has a destructor, and it cleans up the dynamic memory, this is big trouble.
Copy Constructors • So, let’s look at a typical Employee class implementation class Employee {public:Employee(char *name, int id);~Employee();char *getName(){return _name;}//Other Accessor methodsprivate:int id;char *name;}; • See any dynamic memory? Yep.. the name is stored as a char pointer.
Copy Constructors • So, just like the normal constructor, the job of the copy constructor is to initialize the object. • But, instead of arguments to do it, a reference to the object being copied in is given. • So, our function will look like..
Copy Constructors Employee::Employee(Employee &rhs){id = rhs.getId();name = new char[strlen(rhs.getName()) + 1];strcpy(name,rhs.name);} • So, we set the id using the Employee class’s getID method, use the new operator on name to create a new pointer on the heap, and then use strcpy to copy the contents of rhs.name into name.
Overloaded Assignment Ops • Overloading Assigment ( = ) Operators work almost exactly like Copy Constructors, with a few subtle differences • They need to check for self assignment • They return a reference to *this • Depending on your code, they may be different (more optimized) than your copy constructor
Operator Overloading • They need to avoid self assigment – Self Assigment is stuff like Object A; A=A; • Now, normally this would never happen.. but, depending on how pointers are cast, or whatever, it can. So you have to be careful.
Operator Overloading = const Employee &Employee::operator=(const Employee &rhs){ if ( this == &rhs ) return *this; id = rhs.getId();name = new char[strlen(rhs.getName()) + 1];strcpy(name,rhs.name); return *this; }
Rule of 3 • Rule of 3 – Remember if you class needs either a destructor, overloaded assignment operator or copy constructor, it generally needs ALL 3.
Virtual FUNctions • What is a virtual member function? • From an OO perspective, it is the single most important feature of C++ • A virtual function allows derived classes to replace the implementation provided by the base class. • The compiler makes sure the replacement is always called whenever the object in question is actually of the derived class, even if the object is accessed by a base pointer rather than a derived pointer. • This allows algorithms in the base class to be replaced in the derived class, even if users don't know about the derived class.
Virtual Functions • This ability of C++ is called Dynamic Binding. • C++ does static typing anddynamic binding. • Static typingmeans that the legality of a member function invocation is checked at the earliest possible moment: by the compiler at compile time. The compiler uses the static type of the pointer to determine whether the member function invocation is legal. If the type of the pointer can handle the member function, certainly the pointed-to object can handle it as well. • E.g., if Vehicle has a certain member function, certainly Car also has that member function since Car is a kind-of Vehicle.
Virtual Functions • Dynamic binding means that the address of the code in a member function invocation is determined at the last possible moment: based on the dynamic type of the object at run time. • It is called "dynamic binding" because the binding to the code that actually gets called is accomplished dynamically (at run time). Dynamic binding is a result of virtual functions.
Virtual Functiom • So, we change our getSalary methods in both Employee and Manager to be of type virtual. • We do this by just placing the keyword virtual in front of the function name in the class definition
Virtual Functions class Employee { public: virtual int getSalary(); … }; class Manager{ public: virtual int getSalary(); … }; int Employee::getSalary() { return salary; } int Manager::getSalary() { return salary + bonus; }
Virtual Functions Manager mg1 = new Manager(50000,10000); Manager mg2 = new Manager(75000,15000); Employee em1 = new Employee(45000); Employee em2 = new Employee(65000); Employee* payroll[4]; payroll[0]= &mg1; payroll[1]= &em1; payroll[2]= &mg2; payroll[3]= &em2; for (int i=0; i<4; i++) cout<<payroll[i]->getSalary()<<““; //Magically calls correct version, //even thought all Employee* output: 60000 45000 90000 65000
Virtual Functions • So magically, it calls the right version of the function, even if it’s pointed to by some base class pointer. • So how does it actually do this? Let’s look behind the scenes a little.
Virtual Functions • Non-virtual member functions are resolved statically. That is, the member function is selected statically (at compile-time) based on the type of the pointer (or reference) to the object. • In contrast, virtual member functions are resolved dynamically (at run-time). That is, the member function is selected dynamically (at run-time) based on the type of the object, not the type of the pointer/reference to that object. This is called "dynamic binding." Most compilers use some variant of the following technique: if the object has one or more virtual functions, the compiler puts a hidden pointer in the object called a "virtual-pointer" or "v-pointer." This v-pointer points to a global table called the "virtual-table" or "v-table.“. • That’s how it tells which is which.
Abstract classes • Sometimes, we want to have a class to represent something, but we don’t want anyone to actually create an instance of that class. • We do this because we except people to inherit from our base class, and then create instances of the derived class. • We do this by using a virtual function to create an abstract class.
Abstract classes • The Linnaean classification may be a good example of this.. Kingdom, Phylum, Class, Order and Family would be abstract classes, and Genus_Species would be a derived class which could be instantiated. • To do this, we define a pure virtual function in our class definition • A pure virtual function is one with an initializer of = 0 in it’s declaration virtual void getGenusSpecies() const = 0;
Abstract Classes • This pure virtual function makes the clas an Abstract Base Class. • If you want, you can think of the "= 0;" syntax as if the code were at the NULL pointer. • Thus, the class promises a service to its users, yet the class isn't able to provide any code to fulfill that promise. • This forces any actual object created from a [concrete] class derived from this class to have the indicated member function, even though the base class doesn't have enough information to actually define it yet.