Making Inheritance Work: C++ Issues

Similar documents
Making Inheritance Work: C++ Issues

C++ Programming: Polymorphism

Week 7. Statically-typed OO languages: C++ Closer look at subtyping

QUIZ. Write the following for the class Bar: Default constructor Constructor Copy-constructor Overloaded assignment oper. Is a destructor needed?

15: Polymorphism & Virtual Functions

Overview. Constructors and destructors Virtual functions Single inheritance Multiple inheritance RTTI Templates Exceptions Operator Overloading

CS153: Compilers Lecture 11: Compiling Objects

CS152: Programming Languages. Lecture 23 Advanced Concepts in Object-Oriented Programming. Dan Grossman Spring 2011

HAS-A Relationship. Association is a relationship where all objects have their own lifecycle and there is no owner.

HAS-A Relationship. Association is a relationship where all objects have their own lifecycle and there is no owner.

Cpt S 122 Data Structures. Course Review Midterm Exam # 2

POLYMORPHISM 2 PART Abstract Classes Static and Dynamic Casting Common Programming Errors

Dynamic Binding C++ Douglas C. Schmidt

Increases Program Structure which results in greater reliability. Polymorphism

Polymorphism. Zimmer CSCI 330

POLYMORPHISM 2 PART. Shared Interface. Discussions. Abstract Base Classes. Abstract Base Classes and Pure Virtual Methods EXAMPLE

Introduction to C++ Introduction to C++ Dr Alex Martin 2013 Slide 1

Data Abstraction. Hwansoo Han

C++ Crash Kurs. Polymorphism. Dr. Dennis Pfisterer Institut für Telematik, Universität zu Lübeck

VIRTUAL FUNCTIONS Chapter 10

Overview. Constructors and destructors Virtual functions Single inheritance Multiple inheritance RTTI Templates Exceptions Operator Overloading

Polymorphism. Miri Ben-Nissan (Kopel) Miri Kopel, Bar-Ilan University

Introduction to Object-Oriented Programming

CS558 Programming Languages Winter 2013 Lecture 8

09/02/2013 TYPE CHECKING AND CASTING. Lecture 5 CS2110 Spring 2013

Object Oriented Programming: Inheritance Polymorphism

C++ without Classes. CMSC433, Fall 2001 Programming Language Technology and Paradigms. More C++ without Classes. Project 1. new/delete.

Outline. Java Models for variables Types and type checking, type safety Interpretation vs. compilation. Reasoning about code. CSCI 2600 Spring

Inheritance, Polymorphism and the Object Memory Model

The following is an excerpt from Scott Meyers new book, Effective C++, Third Edition: 55 Specific Ways to Improve Your Programs and Designs.

PROGRAMMING LANGUAGE 2

QUIZ. How could we disable the automatic creation of copyconstructors

And Even More and More C++ Fundamentals of Computer Science

OBJECT ORIENTED PROGRAMMING USING C++

Java Inheritance. Written by John Bell for CS 342, Spring Based on chapter 6 of Learning Java by Niemeyer & Leuck, and other sources.

8 Understanding Subtyping

Fast Introduction to Object Oriented Programming and C++

CS105 C++ Lecture 7. More on Classes, Inheritance

QUIZ. How could we disable the automatic creation of copyconstructors

Programming in C++ Prof. Partha Pratim Das Department of Computer Science and Engineering Indian Institute of Technology, Kharagpur

Object Oriented Software Design II

Object Oriented Software Design II

Critique this Code. Hint: It will crash badly! (Next slide please ) 2011 Fawzi Emad, Computer Science Department, UMCP

Object typing and subtypes

Polymorphism. Hsuan-Tien Lin. OOP Class, April 8, Department of CSIE, NTU. H.-T. Lin (NTU CSIE) Polymorphism OOP 04/08/ / 26

CPSC 427: Object-Oriented Programming

Virtual functions concepts

Overriding Variables: Shadowing

Inheritance and Polymorphism

Inheritance and Polymorphism

377 Student Guide to C++

These are notes for the third lecture; if statements and loops.

First IS-A Relationship: Inheritance

OBJECT ORİENTATİON ENCAPSULATİON

ECE 3574: Dynamic Polymorphism using Inheritance

What is Polymorphism? Quotes from Deitel & Deitel s. Why polymorphism? How? How? Polymorphism Part 1

Rules and syntax for inheritance. The boring stuff

Type Hierarchy. Lecture 6: OOP, autumn 2003

More Relationships Between Classes

CS 251 Intermediate Programming Inheritance

AP COMPUTER SCIENCE JAVA CONCEPTS IV: RESERVED WORDS

CS11 Introduction to C++ Fall Lecture 7

CS111: PROGRAMMING LANGUAGE II

CS107 Handout 37 Spring 2007 May 25, 2007 Introduction to Inheritance

Object Oriented Programming. Java-Lecture 11 Polymorphism

Polymorphism Part 1 1

Lecture 2, September 4

25. Generic Programming

Inheritance and Interfaces

Lesson 10A OOP Fundamentals. By John B. Owen All rights reserved 2011, revised 2014

Data Structures and Other Objects Using C++

Java: introduction to object-oriented features

Java Object Oriented Design. CSC207 Fall 2014

Copying Data. Contents. Steven J. Zeil. November 13, Destructors 2

Extending Classes (contd.) (Chapter 15) Questions:

16 Multiple Inheritance and Extending ADTs

Casting in C++ (intermediate level)

Inheritance. Inheritance Reserved word protected Reserved word super Overriding methods Class Hierarchies Reading for this lecture: L&L

Object-oriented Programming. Object-oriented Programming

Object-Oriented Programming, Iouliia Skliarova

Lecture 36: Cloning. Last time: Today: 1. Object 2. Polymorphism and abstract methods 3. Upcasting / downcasting

CSE 401/M501 Compilers

Absolute C++ Walter Savitch

G52CPP C++ Programming Lecture 10. Dr Jason Atkin

Exercise: Singleton 1

What s Conformance? Conformance. Conformance and Class Invariants Question: Conformance and Overriding

Intro. Scheme Basics. scm> 5 5. scm>

Assignment of Structs

CSE351 Winter 2016, Final Examination March 16, 2016

OO Design with Multiple Inheritance

Programming Language Concepts Object-Oriented Programming. Janyl Jumadinova 28 February, 2017

Resource Management With a Unique Pointer. Resource Management. Implementing a Unique Pointer Class. Copying and Moving Unique Pointers

CS112 Lecture: Extending Classes and Defining Methods

COMP322 - Introduction to C++ Lecture 09 - Inheritance continued

CSE341, Fall 2011, Lecture 26 Summary

CS32 - Week 4. Umut Oztok. Jul 15, Umut Oztok CS32 - Week 4

G52CPP C++ Programming Lecture 13

Part X. Advanced C ++

Programming II (CS300)

CSCI 102L - Data Structures Midterm Exam #1 Fall 2011

Transcription:

Steven Zeil September 19, 2013 Contents 1 Base Class Function Members 2 2 Assignment and Subtyping 3 3 Virtual Destructors 5 4 Virtual Assignment 9 5 Virtual constructors 11 51 Cloning 13 6 Downcasting 15 61 RTTI 17 7 Single Dispatching & VTables 19 71 Single Dispatching 19 1

Recording These slides accompany a recorded video: Play Video 1 Base Class Function Members Base Class Function Members Even if you override a function, the inherited bodies are still available to you class Person { string name; long id; void print(ostream& out) { out << name << " " << id << endl; ; class Student: public Person { string school; void print(ostream& out) { Person::print(out); out << school << endl; Base Class Constructors This technique is often used in constructors so that subclasses will only need to initialize their own new data members: class Person { CS330 2

string name; long id; Person (string n, long i) : name(n), id(i) { ; class Student: public Person { string school; Student (string name, long id, School s) : Person(name, id), school(s) { This is a different use of initialization lists than we have seen before But is still consistent with the idea that the initialization list is actually a list of constructor calls 2 Assignment and Subtyping Implementing Data Member Inheritance Inheritance of data members is achieved by treating all new members as extensions of the base class: class Person { public : s t r i n g name; long id ; ; CS330 3

class Student : public Person { public : s t r i n g school ; Extending Data Members When a compiler processes data member declarations, it assigns a byte offset to each one In real life, these increase by however manybytes are required to store the previous data member In this example, I m going to pretend that each data member takes 4 bytes Inherited members occur at the same byte offset as in the base class 0 4 jones 111110 0 4 8 smith 456782 ODU so code like p->name can translate the same whether p points to a Person or a Student p->name is translated as add 0 to the address in p p->id is translated as add 4 to the address in p And that works for both Smith and Jones! Assignment & Extension CS330 4

In most OO languages, we can do superobj = subobj ; but not subobj = superobj ; 0 4 jones 111110 0 4 8 smith 456782 ODU Assigning to a superclass object discards the extra data Presumably, (Smith, 456782) is still a valid Person Even if it loses the information about Smith being a student * So this at least can be said to make sense, even if it s not 100% safe Assigning to a subclass object requires the system to invent data If we assign Jones to a student object, what value should the system copy into the school? 3 Virtual Destructors Virtual Destructors As we ve seen, subclasses can add new data members What happens if we add a pointer: class Person { string name; long id; CS330 5

; class Student: public Person { string school; class GraduateStudent: public Student { private: Transcript* undergradrecords; GraduateStudent (const GraduateStudent& g); GraduateStudent& operator= (const GradudateStudent&); ~GraduateStudent(); ; GraduateStudent::GraduateStudent (const GraduateStudent& g) : name(gname), id(gid), school(gschool), undergradrecords(new Transcript(*(gundergradRecords)) { GraduateStudent& operator= (const GradudateStudent& g) { if (this!= &g) { Student::operator=(g); delete undergradrecords; undergradrecords = new Transcript(*(gundergradRecords)); CS330 6

return *this; GraduateStudent::~GraduateStudent() { delete undergradrecords; and we don t want to share? Deleting Pointers and Inheritance Consider the following two delete statements: Person* g1 = new GraduuateStudent(); GraduateStudent* g2 = new GraduateStudent(); delete g1; delete g2; // compiler-generated ~Person() is called // ~GraduateStudent() is called Both calls are resolved by compile-time binding Therefore the first delete leaks memory - undergraduaterecords is not cleaned up Fix would seem to be to force dynamic binding on the destructors Making the Destructor Virtual The trick is that this has to be done at the top of the inheritance hierarchy CS330 7

class Person { virtual ~Person() { string name; long id; ; class Student: public Person { string school; class GraduateStudent: public Student { private: Transcript* undergradrecords; ; GraduateStudent (const GraduateStudent& g); GraduateStudent& operator= (const GradudateStudent&); ~GraduateStudent(); even though, at the time we wrote that class, there may have been no obvious need for a destructor this seems to violate the Rule of the Big 3 We ll look at the other two in just a moment So you have to think ahead - if there s any chance of a non-shared pointer being added in a future subclass, make your destructor virtual CS330 8

4 Virtual Assignment Virtual Assignment If subclasses can introduce new data members, should assignment be virtual so that we can guarantee proper copying of those extended data members? Virtual Assignment Example void foo ( Person& p1, const Person& p2 ) { p1 = p2 ; GraduateStudent g1, g2 ; foo ( g1, g2 ) ; If p1 and p2 "really" have undergraduaterecord fields, shouldn t we make sure those get copied properly during assignment? Seems reasonable in this case But it means that assignment and copying will behave very differently, which is likely to catch programmers by surprise What s the Problem with Virtual Assignment? If you try it, the inherited members aren t what you might expect: class Person { CS330 9

virtual ~Person() { virtual Person& operator= (const Person& p); string name; long id; ; class Student: public Person { string school; virtual Person& operator= (const Person& p); // inherited from Person // Student& operator= (const Student& s); // generated by compiler class GraduateStudent: public Student { private: Transcript* undergradrecords; GraduateStudent (const GraduateStudent& g); virtual Person& operator= (const Person& p); // inherited from Person // Student& operator= (const Student& s); // inherited from Student GraduateStudent& operator= (const GradudateStudent&); ~GraduateStudent(); ; You actually wind up with multiple overloaded assignment operators in the subclasses What s the Problem with Virtual Assignment? (cont) To make this work, you will need to implement both the virtual and the normal operators CS330 10

Implementing the virtual one is tricky because you might not get a GraduateStudent on the right: void foo ( Student& s1, const Student& s2 ) { s1 = s2 ; Student s ; GraduateStudent g ; foo ( g, s ) ; / / problem : s has no undergraduaterecords f i e l d Recommendation There s no clear consensus in the C++ community about making assignment virtual I recommend against it just because it s potentially confusing Try to avoid using assignment in situations where the "true" data type on the left is uncertain 5 Virtual constructors Virtual constructors Constructors can never be made virtual This can lead to problems when we need to create a copy CS330 11

Example: evaluating a cell reference class CellReferenceNode: public Expression { // represents a reference to a cell private: CellName value; CellReferenceNode () { <::> // Evaluate this expression virtual Value* evaluate(const SpreadSheet&) const; <::> / / Evaluate t h i s expression Value * CellReferenceNode : : evaluate ( const SpreadSheet& s ) const { Cell * c e l l = s getcell ( value ) ; Value * v = ( Value * ) c e l l >getvalue ( ) ; i f ( v == 0) return new ErrorValue ( ) ; else return v ; We would be better off returning a copy of the spreadsheet cell s value rather than the actual one Each Cell owns (does not share) its Value Cell may therefore delete that Value * don t want to risk some other code doing so CS330 12

But how do we make a copy? Not like this! Value * thecopy = new Value ( * v ) ; How big is a Value? Would lose all data members in v required for its particular subtype of Value Better, but Not the "OO Way" Value * newcopy ; i f ( typeid ( * v ) == typeid ( NumericValue ) ) { newcopy = new NumericValue ( v >getnumericvalue ( ) ) ; else i f ( typeid ( * v ) == typeid ( StringValue ) ) { newcopy = new StringValue ( v >render ( 0 ) ) ; else i f ( typeid ( * v ) == typeid ( ErrorValue ) ) { newcopy = new ErrorValue ( ) ; (We ll see how typeid works shortly) 51 Cloning Cloning Solution is to use a simulated "virtual constructor", generally referred to as a clone() or copy() function CS330 13

Value* CellReferenceNode::evaluate(const SpreadSheet& s) const { Cell* cell = sgetcell(value); Value* v = (Value*)cell->getValue(); if (v == 0) return new ErrorValue(); else return v->clone() ; clone() clone() must be supported by all values: class Value { public : virtual Value * clone ( ) const ; ; Implementing clone() Each subclass of Value implements clone() as a copy construction passed to new Value * NumericValue : : clone ( ) const { return new NumericValue ( * this ) ; CS330 14

Value * StringValue : : clone ( ) const { return new StringValue ( * this ) ; 6 Downcasting Suppose that I want to be able to test any two Values to see if they are equal We ll define "equal" here as meaning that they are the same kind of value and would appear the same when rendered Example: Value::== We want to explicitly require all subclasses of Value to provide this test: class Value { virtual bool operator== ( const Value&) const ; ; The operator == compares two shapes Its signature is: (const Value*, const Value&) bool Inheriting == class NumericValue : public Value { class StringValue : public Value { CS330 15

Both classes inherit the == operator The signatures are (const NumericValue*, const Value&) bool (const StringValue*, const Value&) bool Using the Inherited == NumericValue n1, n2 ; StringValue s1, s2 ; bool b = ( n1 == n2) && ( s1 == s2 ) && (n1 == s1 ) ; The last clause suggests a problem How do we compare values of different subtypes? Should we even allow it? Implementing an asymmetric operator We might implement == for NumericValue as: bool NumericValue : : operator== ( const Value& v ) { return d == v d ; ; But in a call like (n1 == s1), vd does not make sense In fact, this will get a compile error CS330 16

Implementing an asymmetric operator (cont) The problem is that we can easily define bool NumericValue : : operator== ( const NumericValue& v ) but bool NumericValue : : operator== ( const Value& v ) seems impossible, as we cannot anticipate all the values that will ever be defined 61 RTTI Working around the == asymmetry The C++ standard defines a mechanism for RTTI (Run Time Type Information) bool NumericValue : : operator== ( const Value& v ) { i f ( typeid ( v ) == typeid ( NumericValue ) ) { const NumericValue &nv = ( const NumericValue&)v ; return d == nv d ; else return false ; ; Note that typeid() can be applied both to objects and to types But it can only be used with types/objects that have at least one virtual function CS330 17

RTTI: typeid and downcasting RTTI also allows you to test to see if v is from a subclass of NumericValue i f ( typeid ( NumericValue ) before ( typeid ( v ) ) or to perform safe downcasting: NumericValue* np = dynamic_cast<numericvalue*>(&v ) ; i f (np!= 0) { / / v r e a l l y was a NumericValue or / / subclass of NumericValue The term downcasting refers to the fact that we are moving down in hour inheritance hierarchy (assuming we draw the base class at the tops) Upcasting is always safe (and usually is done implicitly) Downcasting can be dangerous if we don t check to see if the object really is waht we think it will be Downcasting Should Not Be a Crutch Downcasting is often a tempting way to patch a poor initial choice of virtual protocol functions 95% of the time, it s a bad idea often leads to subtle, hard to trace bugs Oddly, though, downcasting is far more widely accepted in Java than in C++ CS330 18

7 Single Dispatching & VTables Equality Again Earlier, we looked at the problem of comparing two spreadsheet Values: class Value { virtual bool isequal ( const Value&) const ; ; class NumericValue : public Value { virtual bool isequal ( const Value&) const ; ; We saw that problems are caused by NumericValue::isEqual getting a parameter of type Value& rather than NumericValue& Why is this so hard? Why can t we select the best fit from among: class NumericValue : public Value { virtual bool isequal ( const NumericValue&) const ; virtual bool isequal ( const StringValue &) const ; virtual bool isequal ( const ErrorValue&) const ; ; The answer stems from how dynamic binding is implemented 71 Single Dispatching Single Dispatching Almost all OO languages offer a single dispatch model of message passing: CS330 19

the dynamic binding is resolved according to the type of the single object to which the message was sent ( dispatched ) In C++, this is the object on the left in a call: objfoo() There are times when this is inappropriate But it leads to a fast, simple implementation VTables Each object with 1 or more virtual functions has a hidden data member a1 A::foo() A::bar() b1 a pointer to a VTable for it s class this member is always at a predictable location (eg, start or end of the object) a2 B::foo() A::bar() B::baz() Compiling Virtual Function Declarations Each virtual function in a class is assigned a unique, consecutive index number (*VTable)[i] is the address of the class s method for the i th virtual function CS330 20

Example of VTable Use class A { public : A ( ) ; virtual void foo ( ) ; virtual void bar ( ) ; ; class B : public A { public : B ( ) ; virtual void foo ( ) ; virtual void baz ( ) ; ; A* a =??? ; / / might point to an A or a B object a >foo ( ) ; foo(), bar(), and baz() are assigned indices 0, 1, and 2, respectively Example: VTable Structure CS330 21

The call a->foo() is translated as * ( a >VTABLE [ 0 ] ) ( ) ; a1 A::foo() A::bar() b1 The call a->bar() is translated as * ( a >VTABLE [ 1 ] ) ( ) ; Notice that this works regardless of whether a points to an A object or a B object a2 B::foo() A::bar() B::baz() works in this case means does dynamic binding Note that the call a->baz() would not compile, so we should not have to worry about going past the end of the shorter vtable Implementing RTTI The address of the VTable is a unique identifier for each class known to the compiler This makes the vtable an ideal for implementing RTTI and explains why RTTI is only available for classes with at least one virtual function CS330 22