CS 112 - Week 14 Lecture 1 - 2022-11-29
TODAY WE WILL
* Announcements
* inheritance and the "big-3"
* reminder: types and derived classes
* leading into: polymorphism, and virtual methods,
and dynamic late binding
* prep for next class
* reading: some of this is back to Savitch Ch. 15
====
How DOES inheritance impact the "Big 3"?
destructor, copy constructor, overloaded assignment operator
====
* see Savitch Ch. 15 for a *bit* deeper discussion of this
* we already knew we needed to write constructor(s) for
a derived class;
(and call the base class' constructor as part of that)
* Destructors are also effectively not inherited --
if your derived class needs one (e.g., has dynamic
memory to deallocate), you should write that.
* Copy constructor is not inherited, but the automatically
generated one is OK *unless*, again, dynamic memory
is involved (or pointers that would lead lead to
overly-shallow copies)
* The overloaded assignment operator is also not
inherited -- for a derived class, the automatically
generated one will be the same as if it weren't a
derived class, but if needed for dynamic memory
reasons, you SHOULD define it;
* if YOU DO need, in a derived class, to define
the copy constructor and =,
SHOULD call the base class' version of those
to help:
// copy constructor
MyDerived::MyDerived(const MyDerived& object) : MyBase(object)
{
...
}
// the assignment operator
MyDerived& MyDerived::operator=(const MyDerived& right_side)
{
MyBase::operator=(right_side);
// then handle the data fields particular to MyDerived
}
* what about a destructor, then?
* Savitch: If a base class has a correctly-functioning
destructor, then when the destructor for a derived
class is invoked, the system will AUTOMATICALLY
invoke the destructor for the base class as well
That is, your derived class destructor doesn't
have to call the base class' destructor explicitly;
the derived class destructor just needs to call delete
for dynamic memory for data fields particular to that
derived class;
=====
reminder of types of derived objects
=====
* see types-reminder.cpp -- a ColorPoint is of type
ColorPoint and also of type Point
And so I can put ColorPoint objects in an array of
type Point -- BUT they are then treated as type Point
in method calls.
...this is so-called STATIC binding, also called
EARLY binding
=====
Polymorphism
=====
* generally: ability to associate MULTIPLE meanings
to ONE method name
* specifically, in object-oriented programming:
refers to the abiulity to associate MULTIPLE meanings
to ONE method name by means of a special mechanism
called DYNAMIC binding (also called LATE binding)
...you don't get this by default!
you have to declare such a method in a certain way,
and call it in a certain way;
...the method needs to be declared as a virtual method.
* SO: Savitch: "Thus, polymorphism, late binding, and virtual
[methods] are all really the same topic"...!
* SO....
what's a virtual method?
...in a sense, you might say it is used before it is
defined?!
In a class definition, if you want a method to be
virtual, put the word virtual before its name:
class Blah
{
public:
...
virtual double do_this(); // do_this is now a virtual
// method
...
};
* FUN FACT:
* this word virtual MUST be in front of the
name of the method in the BASE class' declaration --
virtual does NOT have to be in the front of
that method's name in a derived class declaration,
although it CAN be and that's considered good
style to PUT it there;
* what does the word virtual ask the compiler to do?
you are asking that the C++ environment be able to
WAIT until this method is called by a program,
and THEN get the method implementation corresponding
to the calling object
* SO: in Point.h,
we added virtual in front of the headers for
display and to_string
...BUT, to get polymorphism, POINTERS need to be involved.
for example:
Point *point_ptrs[4];
point_ptrs[0] = new Point(1, 1);
point_ptrs[1] = new Point(2, 2);
point_ptrs[2] = new ColorPoint(3, 3, "green");
point_ptrs[3] = new ColorPoint(4, 4, "red");
for (int i=0; i < 4; i++)
{
point_ptrs[i]->display();
}
// remember to free those dynamically-allocated
// Points and ColorPoints
for (int i=0; i < 4; i++)
{
delete point_ptrs[i];
}
=====
NOTE about the "slicing problem"
=====
* (see Savitch for more about this)
* the slicing problem is indeed what you see when you
assign a derived object to a base object variable --
the non-inherited parts are lost, "sliced" off;
* BUT C++ offers a way to treat a derived class object
as a base class object without losing its
derived-class-specific parts.
To do this, we use POINTERS to DYNAMIC OBJECT INSTANCES
(combined with virtual methods)
* see example at end of types-reminder.cpp!