c++ training datascope lawrence d’antonio

198
C++ Training C++ Training Datascope Datascope Lawrence D’Antonio Lawrence D’Antonio Lecture 9 Lecture 9 An Overview of C++: An Overview of C++: What is Typing? What is Typing?

Upload: ivie

Post on 05-Jan-2016

15 views

Category:

Documents


0 download

DESCRIPTION

C++ Training Datascope Lawrence D’Antonio. Lecture 9 An Overview of C++: What is Typing?. Type Systems. “The purpose of a type system is to prevent the occurrence of execution errors during the running of a program.” Cardelli - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: C++ Training Datascope Lawrence D’Antonio

C++ TrainingC++ TrainingDatascopeDatascope

Lawrence D’AntonioLawrence D’Antonio

Lecture 9Lecture 9

An Overview of C++:An Overview of C++:

What is Typing?What is Typing?

Page 2: C++ Training Datascope Lawrence D’Antonio

Type Systems

“The purpose of a type system is to prevent the occurrence of execution errors during the running of a program.” Cardelli

The largest allowed range of values for a program variable is its type.

Languages in which variables have non-trivial types are called typed languages.

Page 3: C++ Training Datascope Lawrence D’Antonio

Type Systems 2

Languages that do not restrict the ranges of variables are called untyped languages.

The -calculus is an example of an untyped language.

Languages can check for type errors either during compile time (static checking) or run-time (dynamic checking).

Page 4: C++ Training Datascope Lawrence D’Antonio

Type Systems 3

There are two types of execution errors. Errors that cause computation to stop are

called trapped errors. Errors that go unnoticed and cause

arbitrary behavior are called untrapped errors.

A program is safe if it does not cause untrapped errors to occur.

Page 5: C++ Training Datascope Lawrence D’Antonio

Type Systems 4

Languages in which programs are safe are called safe languages.

Typed Untyped

Safe ML, Java LISP

Unsafe C Assembler

Page 6: C++ Training Datascope Lawrence D’Antonio

Types of typingTypes of typing

Static typing: Data type determined at compile-time. Type must be declared or inferred.

Dynamic typing: Data type may be determined at run-time. Type need not be declared.

Strong typing: Variables are bound to a specific type.

Weak typing: A variable’s type may change.

Page 7: C++ Training Datascope Lawrence D’Antonio

Types of typing 2Types of typing 2

Soft typing: Type checking occurs, but incorrect types are only given warnings.

Manifest typing: Types are named explicitly in the code.

Page 8: C++ Training Datascope Lawrence D’Antonio

Varieties of typing

Static and strong typing: Java, Pascal, OCaml, Haskell

Static and weak typing: C/C++ Dynamic and strong typing: Python Dynamic and weak typing: PHP

Page 9: C++ Training Datascope Lawrence D’Antonio

Type Systems

Is there a nontrivial type associated with each declaration?

Static typing = yes, Dynamic typing = no, Soft typing = optional

If there is, are these types declared explicitly in the source code?

Manifest typing = yes, Type Inference = optional

Page 10: C++ Training Datascope Lawrence D’Antonio

Type Systems 2

Does the possibility of a type failure cause a compile-time error?

Static typing = yes, Dynamic or soft typing = no

Is the type system strictly enforced, with no loopholes or unsafe casts?

Strongly typed = yes, Weak typing = no

Page 11: C++ Training Datascope Lawrence D’Antonio

Benefits of a Type System

Safety: The use of a type system allows the compiler to detect invalid or meaningless code. For example, the code

x = 5/”Hello”

will be caught as illegal.

Page 12: C++ Training Datascope Lawrence D’Antonio

Benefits of a Type System 2

Optimization: a compiler can use type information in various ways to improve a program.

For example, knowing values of a certain type must align at a multiple of 4 may let the compiler use more efficient machine instructions.

Also, a type system allows the compiler to select appropriate code.

Page 13: C++ Training Datascope Lawrence D’Antonio

Benefits of a Type System 3

Documentation: the use of type can be documentation of the programmer’s intent.

Type annotation documents how program objects are to be used.

Page 14: C++ Training Datascope Lawrence D’Antonio

Benefits of a Type System 4

Abstraction: the ability to name types allows the programmer to think about programs at a higher level.

The hiding of type details lets the programmer directly model the problem domain.

The correctness of a program does not depend on the implementation of types.

Page 15: C++ Training Datascope Lawrence D’Antonio

Static typing

Also known as early binding. Under static typing, the method to be

called is the one associated with the type of the formal parameter.

The binding can occur as soon as the type of the formal parameter is known.

C++ uses static typing by default.

Page 16: C++ Training Datascope Lawrence D’Antonio

Static typing 2

A reference value is a program construct that is a value and can have a value. It can have different values at different times.

In static typing a reference is constrained with respect to the type of value denoted by the reference.

In a statically typed language one relies upon the compiler to do type checking.

Page 17: C++ Training Datascope Lawrence D’Antonio

Static typing 3

There are two ways to implement static typing.

One method is type inference. Here the type of an expression is inferred through analysis of the program.

Another method is manifest typing. Here objects must be declared with a type annotation.

Page 18: C++ Training Datascope Lawrence D’Antonio

Type inference

Many languages use type inference exclusively or in part. For example, the languages Boo, C# 3.0, Cayenne, Chrome, Clean, Cobra, D, Epigram, F#, Haskell, ML, Nemerle, OCaml, Scala use type inference.

Page 19: C++ Training Datascope Lawrence D’Antonio

Type inference 2

C# 3.0 example

var x = “Hello”; //x is deduced to be a string

//More complex example

var custQuery = from cust in customers where cust.City == "Phoenix"

select new { cust.Name, cust.Phone };

Page 20: C++ Training Datascope Lawrence D’Antonio

Type inference 3

Boo example. The type of an array is the least generic type that could store all elements in the array.

a = (1, 2) # a is of type (int)

b = (1L, 2) # b is of type (long)

c = ("foo", 2) # c is of type (object)

Page 21: C++ Training Datascope Lawrence D’Antonio

Type inference 4

Haskell example apply f v = f v The inferred type for apply is (a -> b) -> a -> b Namely apply is a function that takes a

function taking an argument of type a and returning a value of type b and applies the function to the argument, returning a b.

Page 22: C++ Training Datascope Lawrence D’Antonio

Type inference 5

C++ uses type inference.

For example, template parameters are deduced from the argument types in a function call.

Page 23: C++ Training Datascope Lawrence D’Antonio

Type inference 6

C++ ‘09 will have a greater use of type inference. For example:

for(auto p = v.begin();

p! = v.end(); ++p)

cout << *p << endl;

Page 24: C++ Training Datascope Lawrence D’Antonio

Type inference 7

Chrome example

var u := from u in lUsers where u.Age = 35 order by u.Name;

Here lUsers is a collection of objects of unnamed type.

Page 25: C++ Training Datascope Lawrence D’Antonio

Type inference 8

Cobra exampleclass Foo def bar

i = 1_000_000 for j = 0 .. i

doSomething(j)

def doSomething(i as int) pass

Page 26: C++ Training Datascope Lawrence D’Antonio

Type inference 9

F# examplelet asynctask = async {

let req = WebRequest.Create(url)

let! response = req.GetResponseAsync()

use stream = response.GetResponseStream()

use streamreader = new

System.IO.StreamReader(stream)

return streamreader.ReadToEnd()

}

Page 27: C++ Training Datascope Lawrence D’Antonio

Type inference 10

Nemerle example

def d = Dictionary ();

d.Add ("Ala", 7);

foreach (s in args) { ... }

Page 28: C++ Training Datascope Lawrence D’Antonio

Dynamic typing

In dynamic typing, type checking normally occurs at run time.

Operations are checked just before they are performed.

For example, the code for the + operator may check the types of its operands just before the addition is performed.

Page 29: C++ Training Datascope Lawrence D’Antonio

Dynamic typing 2

For example, if the operands for the + operator are both integers then integer addition is performed.

If one operand is an integer and the other a floating point number then floating point addition is performed.

If one operand is an integer and the other a string then an exception is raised.

Page 30: C++ Training Datascope Lawrence D’Antonio

Dynamic typing 3

Dynamically typed languages are more flexible than statically typed languages.

The problem for statically determining for an arbitrary program whether or not a type error will occur at run time is undecidable.

Therefore sound static type checkers will determine some programs as potentially unsafe that would actually execute without a type error.

Page 31: C++ Training Datascope Lawrence D’Antonio

Dynamic typing 4

Static typing finds type errors at compile time.

Advocates of strongly statically typed languages such as ML and Haskell claim that most program errors are type errors. Namely, most errors would not occur if types were used in the correct manner by the programmer.

Page 32: C++ Training Datascope Lawrence D’Antonio

Dynamic typing 5

Static typing usually results in compiled code that executes more quickly than dynamic typing.

On the other hand, dynamic typing can reduce compile time and speed up the software development cycle.

Page 33: C++ Training Datascope Lawrence D’Antonio

Dynamic typing 6

Metaprogramming is the ability of a computer program to use or manipulate other programs (including itself) as its data.

Dynamic typing usually makes metaprogramming easier. For example, templates in C++ are more cumbersome than equivalent code in Python or Ruby.

Page 34: C++ Training Datascope Lawrence D’Antonio

Metaprogramming

Metaprogramming is a facility provided by many languages.

Programs in Lisp, Python, Ruby, Smalltalk, PHP, REBOL, Perl, Tcl, Lua, and JavaScript are modifiable at run time.

The language of a metaprogram is called its metalanguage.

Page 35: C++ Training Datascope Lawrence D’Antonio

Metaprogramming 4

The ability of a programming language to be its own metalanguage is called reflection.

Other types of metaprogramming include: Generative programming which involves

one program generating another. A quine which is a program that outputs

itself.

Page 36: C++ Training Datascope Lawrence D’Antonio

Metaprogramming 5

Forth which is a self-compiling language. The programmer can modify the compiler.

A compiler is an example of a metaprogramming tool for translating high-level programs into machine code.

The compiler-compiler yacc is a metaprogramming tool to generate a tool for translating high level programs into machine code.

Page 37: C++ Training Datascope Lawrence D’Antonio

Metaprogramming 6

A quine in Atlas Autocode%BEGIN

!THIS IS A SELF-REPRODUCING PROGRAM

%ROUTINESPEC R

R

PRINT SYMBOL(39)

R

PRINT SYMBOL(39)

NEWLINE

%CAPTION %END~

%CAPTION %ENDOFPROGRAM~

Page 38: C++ Training Datascope Lawrence D’Antonio

Metaprogramming 7 Quine continued

%ROUTINE R %PRINTTEXT ' %BEGIN !THIS IS A SELF-REPRODUCING PROGRAM %ROUTINESPEC R R PRINT SYMBOL(39) R PRINT SYMBOL(39) NEWLINE %CAPTION %END~ %CAPTION %ENDOFPROGRAM~ %ROUTINE R %PRINTTEXT ' %END %ENDOFPROGRAM

Page 39: C++ Training Datascope Lawrence D’Antonio

Metaprogramming 8

Quine example in C#include <stdio.h>

int main(int argc, char** argv)

{

/* This macro B will expand to its argument, followed by a printf command that prints the macro invocation as a literal string */

#define B(x) x; printf(" B(" #x ")\n");

Page 40: C++ Training Datascope Lawrence D’Antonio

Metaprogramming 9

Quine C example continued/*This macro A will expand to a printf command that prints the macro invocation, followed by the macro argument itself. */

#define A(x) printf(" A(" #x ")\n"); x;

/* Now we call B on a command to print the text of the program up to this point. It will execute the command, and then cause itself to be printed. */

Page 41: C++ Training Datascope Lawrence D’Antonio

Metaprogramming 10

B(printf("#include <stdio.h>\n\nint main(int argc, char** argv)\n{\n/*This macro B will expand to its argument, followed by a printf\n command that prints the macro invocation as a literal string */\n#define B(x) x; printf(\" B(\" #x \")\\n\");\n\n/* This macro A will expand to a printf command that prints the macro invocation,\n followed by the macro argument itself. */\n#define A(x) printf(\" A(\" #x \")\\n\"); x;\n\n/* Now we call B on the text of the program\n up to this point. It will execute the command, and then cause\n itself to be printed. */\n"))

Page 42: C++ Training Datascope Lawrence D’Antonio

Metaprogramming 11

A(printf("/* Lastly, we call A on a command to print the remainder of the program;\n it will cause itself to be printed, and then execute the command. */\n}\n")) /* Lastly, we call A on a command to print the remainder of the program; it will cause itself to be printed, and then execute the command. */ }

Page 43: C++ Training Datascope Lawrence D’Antonio

Metaprogramming 12

An extreme example of metaprogramming is language-oriented programming.

To solve a problem using language-oriented programming, one doesn’t use a general-purpose language.

Instead the programmer creates a domain specific programming language.

Page 44: C++ Training Datascope Lawrence D’Antonio

Metaprogramming 13

Two types of metaprogramming.

One: expose the internals of the run time engine through APIs.

Two: dynamic execution of strings containing program commands.

Page 45: C++ Training Datascope Lawrence D’Antonio

Metaprogramming 14

Metaprogramming example (in bash).#!/bin/bash

# metaprogram

echo '#!/bin/bash' >program

for ((I=1; I<=992; I++)) do

echo "echo $I" >>program

done

chmod +x program

Page 46: C++ Training Datascope Lawrence D’Antonio

Ruby Example

Suppose we want to read a CSV file “people.txt”

name,age,weight,height "Smith, John", 35, 175, "5'10" "Ford, Anne", 49, 142, "5'4" "Taylor, Burt", 55, 173, "5'10" "Zubrin, Candace", 23, 133, "5'6"

Page 47: C++ Training Datascope Lawrence D’Antonio

Ruby Example 2

# file: my-csv.rb class DataRecord

def self.make(file_name) data = File.new(file_name) header = data.gets.chomp data.close class_name =

File.basename(file_name,".txt").capitalize # "foo.txt" => "Foo" klass = Object.const_set(class_name,Class.new) names = header.split(",")

Page 48: C++ Training Datascope Lawrence D’Antonio

Ruby Example 3

klass.class_eval do

attr_accessor *names

define_method(:initialize) do |*values|

names.each_with_index do |name,i|

instance_variable_set("@"+name, values[i]) end

end

Page 49: C++ Training Datascope Lawrence D’Antonio

Ruby Example 4

Still inside klass.class_eval

define_method(:to_s) do

str = "<#{self.class}:"

names.each

{|name| str << "#{name}=#{self.send(name)}" }

str + ">"

end

alias_method :inspect, :to_s

end

Page 50: C++ Training Datascope Lawrence D’Antonio

Ruby Example 5

def klass.read

array = []

data = File.new(self.to_s.downcase+".txt") data.gets # throw away header

data.each do |line|

line.chomp!

values = eval("[#{line}]")

array << self.new(*values)

end

data.close array

end

Page 51: C++ Training Datascope Lawrence D’Antonio

Ruby Example 6

Driver program

require 'my-csv'

DataRecord.make("people.txt") # Ignore return value list = People.read # refer to the class by name

puts list[0]

# Output:

# <People: name=Smith, John age=35 weight=175 height=5'10>

Page 52: C++ Training Datascope Lawrence D’Antonio

Ruby Example 7

Attributes are first-class citizensperson = list[0] puts person.name # Smith, John if person.age < 18 puts "under 18"

else puts "over 18" # over 18

end kg = person.weight / 2.2 # kilograms

Page 53: C++ Training Datascope Lawrence D’Antonio

Dynamic typing example# Python example

class Cat: def speak(self): print "meow!"

class Dog: def speak(self): print "woof!"

class Bob: def speak(self): print "hello world!"

def command(pet): pet.speak()

pets = [ Cat(), Dog(), Bob() ]

for pet in pets: command(pet)

Page 54: C++ Training Datascope Lawrence D’Antonio

Strongly typed

Different definitions of strongly typed. A language is strongly typed if: type annotations are associated with

variable names, rather than with values. If types are attached to values, it is weakly typed.

it contains compile-time checks for type constraint violations. If checking is deferred to run time, it is weakly typed.

Page 55: C++ Training Datascope Lawrence D’Antonio

Strongly typed 2

A language is strongly typed if: there are compile-time or run-time checks

for type constraint violations. If no checking is done, it is weakly typed.

conversions between different types are forbidden. If such conversions are allowed, it is weakly typed.

Page 56: C++ Training Datascope Lawrence D’Antonio

Strongly typed 3

A language is strongly typed if: conversions between different types must

be indicated explicitly. If implicit conversions are performed, it is weakly typed.

there is no language-level way to disable or evade the type system. If there are casts or other type-evasive mechanisms, it is weakly typed.

Page 57: C++ Training Datascope Lawrence D’Antonio

Strongly typed 4

A language is strongly typed if: it has a complex, fine-grained type system

with compound types. If it has only a few types, or only scalar types, it is weakly typed.

the type of its data objects is fixed and does not vary over the lifetime of the object. If the type of a datum can change, the language is weakly typed.

Page 58: C++ Training Datascope Lawrence D’Antonio

Strongly typed 5

A language is strongly typed if: The language implementation is required

to provide a type checker that ensures that no type errors will occur at run time.

For example, the types of operands are checked in order validate an operation.

Page 59: C++ Training Datascope Lawrence D’Antonio

Is this legal?

main() { //Converts float to string of 4 charsunsigned char *c; float f = 10;

for (c = (char *)&f; c < sizeof(float) + (char *)&f; c++)

{ std::cout << *c;} std::cout<< ‘\n’; return 0;

}

Page 60: C++ Training Datascope Lawrence D’Antonio

No this is not considered legal by the compiler. There is a type error.

type.cpp:8: error: invalid conversion from `char*' to `unsigned char*'type.cpp:8: error: comparison between distinct pointer types `unsigned char*' and `char*' lacks a cast

The compiler is complaining about the comparison:

c < sizeof(float) + (char *)&f

Answer part 1

Page 61: C++ Training Datascope Lawrence D’Antonio

Answer part 2

The compiler is saying that the left side is unsigned char *, but the right side is char *. Hence the type error.

Why is this an error?

The sizes of char and unsigned char are the same. So it should be safe to compare pointers to these types.

Page 62: C++ Training Datascope Lawrence D’Antonio

Answer part 3

Why not give a warning instead of an error?

More importantly. The compiler balks at the pointer comparison, but says nothing about the assignment

c = (char*)&f

It would seem that both expressions should be treated the same.

Page 63: C++ Training Datascope Lawrence D’Antonio

Answer part 4

But if you change to

for (c = (char *)&f; c < sizeof(float) + (unsigned char *)&f; c++) { std::cout << *c; }

The compiler is then happy with the comparison, but states “invalid conversion from `char*' to `unsigned char*‘ “ about the assignment c = (char *)&f.

Page 64: C++ Training Datascope Lawrence D’Antonio

Answer part 5

If you change the example to:

unsigned char *c;

float f = 10;

for (c = (unsigned char *)&f;

c < sizeof(float) + (unsigned char *)&f;

c++) { std::cout << *c; }

Page 65: C++ Training Datascope Lawrence D’Antonio

Answer part 6

Then the compiler is happy and the program has the output: A

If you use float f = 100340.567; instead, then the output is: GÃúI

Page 66: C++ Training Datascope Lawrence D’Antonio

Answer part 7

Note: it would be a type error to compare char and unsigned char.

This is an example of the conservative nature of static typing.

All in all, this example illustrates strong and weak typing.

Page 67: C++ Training Datascope Lawrence D’Antonio

Duck typing

“If it looks like a duck and quacks like a duck then it’s a duck.”

In dynamically typed languages such as Ruby and Python, polymorphism can be achieved without the use of inheritance.

Page 68: C++ Training Datascope Lawrence D’Antonio

Duck typing 2

Pseudo-code example:function calculate(a, b, c) =>

return (a+b)*c a = calculate(1, 2, 3) b = calculate([1, 2, 3], [4, 5, 6], 2) c = calculate('apples ', 'and oranges, ', 3)

print to_string a print to_string b print to_string c

Page 69: C++ Training Datascope Lawrence D’Antonio

Duck typing 3

This example illustrates the use of polymorphism without inheritance. It works for any types such as int, list, string so long as the types support + and *The output is:

9

[1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]

apples and oranges, apples and oranges, apples and oranges,

Page 70: C++ Training Datascope Lawrence D’Antonio

Duck typing 4

How would you do this example in C++?

Would you use inheritance or templates?

Page 71: C++ Training Datascope Lawrence D’Antonio

Duck typing 5

template<class T, class U>

T calculate(const T &a, const T &b,

const U &c)

{

return (a+b)*c;

}

Page 72: C++ Training Datascope Lawrence D’Antonio

Weak typing example

var x := 5;

var y := "37";

Print(x + y);

In Visual Basic this prints: 42

In JavaScript this prints: 537

Page 73: C++ Training Datascope Lawrence D’Antonio

Type and Class Hierarchies

There are four kinds of inheritance: Substitution inheritance Inclusion inheritance Constraint inheritance Specialization inheritance

Page 74: C++ Training Datascope Lawrence D’Antonio

Substitution inheritance

We say that a type t inherits from a type t' if we can perform more operations on objects of type t than on objects of type t'.

This means that every place an object of type t' occurs we can substitute for it an object of type t.

Page 75: C++ Training Datascope Lawrence D’Antonio

Substitution inheritance 2

This means that objects of type t extend objects of type t'.

Page 76: C++ Training Datascope Lawrence D’Antonio

Open-Closed Principle

From Bertran Meyer,

“Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.”

Page 77: C++ Training Datascope Lawrence D’Antonio

Open-Closed Principle 2

“Open for extension” means that the behavior of an object can be extended. The object may be made to behave in new and different ways as the requirements of the application change or to meet the needs of a new application.

Page 78: C++ Training Datascope Lawrence D’Antonio

Open-Closed Principle 3

“Closed for modification” means that the source code of the object cannot change.

Programs conforming to the open-closed principle are modified by adding code rather than changing existing code.

Page 79: C++ Training Datascope Lawrence D’Antonio

Open-Closed Principle 4

Suppose we have a Shape class hierarchy. Let us define a function:

void DrawAllShapes(set<Shape*> &s) {

std::set<Shape*>::iterator p;

for(p = s.begin(); p != s.end(); p++)

(*p)->draw();

}

Page 80: C++ Training Datascope Lawrence D’Antonio

Open-Closed Principle 5

The DrawAllShapes function satisfies the open-closed principle. If additional shapes were added to the Shape hierarchy this function would not need to be modified.

On the other hand, if instead, type fields had been used then the function would violate the principle (additional shapes would require new cases in the code).

Page 81: C++ Training Datascope Lawrence D’Antonio

LSP

The Liskov Substitution Principle states

“If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.”

Page 82: C++ Training Datascope Lawrence D’Antonio

LSP 2

Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

A function that violates LSP will also violate the Open-Closed Principle.

Page 83: C++ Training Datascope Lawrence D’Antonio

LSP 3

class Rectangle{public:void SetWidth(double w) {itsWidth = w;}void SetHeight(double h) {itsHeight = h;}double GetHeight() const {return itsHeight;}double GetWidth() const {return itsWidth;}

private:double itsWidth;double itsHeight;

};

Page 84: C++ Training Datascope Lawrence D’Antonio

LSP 4

What if we want to define a Square class that is derived from Rectangle?

The Square inherits the width and height data members.

The Square doesn’t need data members for both width and height. But it inherits both.

One must write code that keeps the height and width equal.

Page 85: C++ Training Datascope Lawrence D’Antonio

LSP 5class Square : public Rectangle{public:

void SetWidth(double w);void SetHeight(double h);

};

void Square::SetWidth(double w){

Rectangle::SetWidth(w); Rectangle::SetHeight(w);}

void Square::SetHeight(double h){

Rectangle::SetHeight(h); Rectangle::SetWidth(h);}

Page 86: C++ Training Datascope Lawrence D’Antonio

LSP 6

Now the Square remains mathematically valid.

Square s;

s.SetWidth(1); //Sets width & height to 1

s.SetHeight(2); //Sets width & height to 2

Page 87: C++ Training Datascope Lawrence D’Antonio

LSP 7

But what happens with the following code?

void f(Rectangle& r){

r.SetWidth(32);}//...Square s;s.SetHeight(3);f(s);

Page 88: C++ Training Datascope Lawrence D’Antonio

LSP 8

Bad things happen with this code!

Square s;s.SetHeight(3)f(s); After this code executes, Square s has

height = 3 and width = 32. So it’s no longer a Square!

Page 89: C++ Training Datascope Lawrence D’Antonio

LSP 9

Of course the problem is that SetHeight() and SetWidth() are not virtual in Rectangle.

We can change Rectangle so as to make these functions virtual, but then the creation of the child (the Square) causes a change in the parent (the Rectangle).

Page 90: C++ Training Datascope Lawrence D’Antonio

LSP 10

This violates the Open-Closed Principle. Rectangle should be open for extension (Square) but not for modification.

Perhaps these functions should have been made virtual when Rectangle was defined?

But that would have required that Rectangle anticipated Square.

Page 91: C++ Training Datascope Lawrence D’Antonio

LSP 11

Change Rectangle as shown below, but leave Square unchanged.

class Rectangle{public:

virtual void SetWidth(double w) {itsWidth = w;}virtual void SetHeight(double h) {itsHeight = w;}double GetHeight() const {return itsHeight;}double GetWidth() const {return itsWidth;}

private:double itsWidth;double itsHeight;

};

Page 92: C++ Training Datascope Lawrence D’Antonio

LSP 12

We now seem to have a self consistent model of the concepts of rectangle and square.

Whatever you do to a Square object, it behaves like a square.

Whatever you do to a Rectangle object it behaves like a rectangle.

Page 93: C++ Training Datascope Lawrence D’Antonio

LSP 13

But a model that is self consistent is not necessarily consistent from the point of view of clients.

The following function illustrates a violation of the Liskov Substitution Principle and shows the fundamental flaw in the design.

Page 94: C++ Training Datascope Lawrence D’Antonio

LSP 14

void g(Rectangle& r)

{

r.SetWidth(5);

r.SetHeight(4);

assert(r.GetWidth() * r.GetHeight()

== 20);

}

Page 95: C++ Training Datascope Lawrence D’Antonio

LSP 15

What’s wrong with this function? If it is passed a Rectangle then the

assertion will be true. If it is passed a Square then the assertion

will be false. Was the programmer who wrote that

function justified in assuming that changing the width of a Rectangle leaves its height unchanged?

Page 96: C++ Training Datascope Lawrence D’Antonio

LSP 16

The assumption seems reasonable considering the nature of rectangles.

So there are functions that take pointers or references to Rectangle objects, but cannot operate properly upon Square objects.

This is a violation of LSP. One cannot substitute a Square object for a Rectangle in function g().

Page 97: C++ Training Datascope Lawrence D’Antonio

LSP 17

A model, viewed in isolation, can not be meaningfully validated. The validity of a model can only be expressed in terms of its clients.

Square and Rectangle are self consistent but when we looked at them from the viewpoint of a programmer who made reasonable assumptions about the base class, the model broke down.

Page 98: C++ Training Datascope Lawrence D’Antonio

LSP 18

What went wrong? Isn’t a Square a type of Rectangle?

No! The behavior of a Square object is not consistent with the behavior of a Rectangle object.

The is-a relationship pertains to extrinsic public behavior ; behavior that clients may depend on.

Page 99: C++ Training Datascope Lawrence D’Antonio

LSP 19

For example, the function g() defined previously depends on the height and width varying independently of one another.

In order for the LSP to hold, all derived classes must conform to the behavior that clients expect of the base classes that they use.

Page 100: C++ Training Datascope Lawrence D’Antonio

LSP 20

Relationship between LSP and Design by Contract.

In Design by Contract, methods declare pre- and post-conditions.

Preconditions must be true in order for the method to execute.

Upon completion, the method guarantees that the postcondition will be true.

Page 101: C++ Training Datascope Lawrence D’Antonio

LSP 21

Bertrand Meyer: “When redefining a routine [in a derived class], you may only replace its precondition by a weaker one, and its postcondition by a stronger one.”

Page 102: C++ Training Datascope Lawrence D’Antonio

LSP 22

In the example, Rectangle::SetWidth() has the postcondition that the height of the rectangle is unchanged.

While Square::SetWidth() has replaced that postcondtion with one guaranteeing that the width and height are equal.

Square::SetWidth() has violated the contract of the base class.

Page 103: C++ Training Datascope Lawrence D’Antonio

Inclusion inheritance

Corresponds to the concept of classification.

It states that t is subtype of t ', if every object of type t is also an object of type t '. This type of inheritance is based on structure and not on operations.

Page 104: C++ Training Datascope Lawrence D’Antonio

Constraint inheritance

A type t is a subtype of a type t ', if it consists of all objects of type t which satisfy a given constraint. An example of such a inheritance is that teenager is a subclass of person: teenagers don't have any more fields or operations than persons but they obey more specific constraints (their age is restricted to be between 13 and 19).

Page 105: C++ Training Datascope Lawrence D’Antonio

Specialization inheritance

A type t is a subtype of a type t ', if objects of type t are objects of type t which contains more specific information. Examples of such are employees and managers where the information for managers is that of employees together with extra fields.

Page 106: C++ Training Datascope Lawrence D’Antonio

Type vs. Class

Classes are templates for creating objects, providing initial values for instance variables and the bodies for methods.

Types are abstractions that represent sets of values and the operations and relations applicable to them.

Page 107: C++ Training Datascope Lawrence D’Antonio

Type vs. Class 2

Types should hide implementation details. Instead they should only reveal the names

and signatures of the messages that may be sent to them.

Distinct classes with the same public methods and types generate objects of the same type.

Page 108: C++ Training Datascope Lawrence D’Antonio

Type vs. Class 3

Here is an example

class A {private: std::string s; int n;public: A(const std::string &str):

s(str), n(str.size()) {} void foo(int a) { n += a; s.resize(n); } std::string get() { std::cout << s.size() << '\n'; return s; }};

Page 109: C++ Training Datascope Lawrence D’Antonio

Type vs. Class 4class B {private: std::string t; bool f() { return t == "Hello"; }public: B(const std::string &s): t(s) { if (f()) std::cout << "World\n"; } void foo(int a) { std::string temp(t); for(int i = 0; i < a; i++) t += temp; } std::string get() { return t; }};

Page 110: C++ Training Datascope Lawrence D’Antonio

Type vs. Class 5

main(){ A a("World"); B b("Hello"); a.foo(2); std::cout << a.get() << '\n'; b.foo(3); std::cout << b.get() << '\n'; return 0;}

Page 111: C++ Training Datascope Lawrence D’Antonio

Type vs. Class 6

Objects of classes A and B are the same type.

Objects of different classes may be used interchangeably and simultaneously as long as they have the same object type.

Page 112: C++ Training Datascope Lawrence D’Antonio

Type vs. Class 7

We say that T is a subtype of U if a value of type T can be used in any context in which a value of type U is expected.

A value of type T can masquerade as an element of type U in all contexts.

A subclass may be defined by either adding or modifying methods and instance variables of the original type.

Page 113: C++ Training Datascope Lawrence D’Antonio

Type vs. Class 8

Subtyping depends only on the types or interfaces of values, while inheritance depends on implementations.

A subtype is not necessarily a subclass. If an object of type T has at least all of the

methods of type U, and the corresponding methods have the same type, then an object of type T can masquerade as an object of type U.

Page 114: C++ Training Datascope Lawrence D’Antonio

Type vs. Class 9

Class defines structure, while type abstracts a similarity.

For example, Benjamin Franklin and Ohio are two classes of submarines.

Both classes are the same type of submarine, namely, they are both ballistic missile submarines.

Page 115: C++ Training Datascope Lawrence D’Antonio

Type vs. Class 10

In STL, a fundamental concept is that of a Container.

Sequence Container and Associative Container are types of Container.

Page 116: C++ Training Datascope Lawrence D’Antonio

Type vs. Class 11

But there are no C++ classes called Sequence Container and Associative Container.

In STL, vector, list, and deque are Sequence Container classes.

They implement a specific form of Sequence Container.

Page 117: C++ Training Datascope Lawrence D’Antonio

Is this legal?

class A {};

class B: public A {};

Page 118: C++ Training Datascope Lawrence D’Antonio

Example part 2

class C {public: virtual A foo() {

std::cout << "A::foo()\n";return A(); }

};

class D: public C {public: int foo() {

std::cout << "B::foo()\n"; return 5; }

};

Page 119: C++ Training Datascope Lawrence D’Antonio

Example part 3

main() { C *p = new C; p->foo();

p = new D; p->foo(); return 0;}

Page 120: C++ Training Datascope Lawrence D’Antonio

Not legal!

conflicting return type specified for `virtual int D::foo()‘ overriding `virtual A C::foo()'

Violation on rules for covariant return types in C++.

Page 121: C++ Training Datascope Lawrence D’Antonio

Is this legal?

class C {public: virtual B foo() {

std::cout << "A::foo()\n";return B(); }

};

class D: public C {public: A foo() {

std::cout << "B::foo()\n"; return A(); }

};

Page 122: C++ Training Datascope Lawrence D’Antonio

Not legal!

invalid covariant return type for `virtual A D::foo()' overriding `virtual B C::foo()'

Again, incorrect return type for overriding function.

Page 123: C++ Training Datascope Lawrence D’Antonio

Covariant types

A covariant operator preserves the ordering of types.

For example, array types are covariant.

So that if T is a subtype of U then Array[T] is a subtype of Array[U].

Page 124: C++ Training Datascope Lawrence D’Antonio

Covariant types 2

In C++, return types are covariant.

Namely, if Base::foo() is virtual then the return type of Der::foo() must be a subtype of Base::foo().

The derived class can only narrow return types, it cannot otherwise change them.

Page 125: C++ Training Datascope Lawrence D’Antonio

Is this legal?

class C {public: virtual A foo() {

std::cout << "A::foo()\n";return A(); }

};

class D: public C {public: B foo() {

std::cout << "B::foo()\n"; return B(); }

};

Page 126: C++ Training Datascope Lawrence D’Antonio

Not legal!

invalid covariant return type for `virtual B D::foo()‘ overriding `virtual A C::foo()'

Why is this? Isn’t B a subtype of A?

Not for return by value.

Page 127: C++ Training Datascope Lawrence D’Antonio

Is this legal?

class C {public: virtual A* foo() {

std::cout << "A::foo()\n";return new A(); }

};

class D: public C {public: B* foo() {

std::cout << "B::foo()\n"; return new B(); }

};

Page 128: C++ Training Datascope Lawrence D’Antonio

Yes this is legal.

This is a valid use of covariant return types.

Page 129: C++ Training Datascope Lawrence D’Antonio

Contravariant types

A contravariant operator reverses the order of types.

For example, argument types are contravariant.

A function that is expecting a Base& may be passed a subtype but not a supertype.

Page 130: C++ Training Datascope Lawrence D’Antonio

Contravariant types 2

The general relationship between functions and types can be expressed:

If and then

This is called the contravariant rule.

1 2 1 2S S T T

2 2S T1 1T S

Page 131: C++ Training Datascope Lawrence D’Antonio

Contravariant types 3

This rules states that if T1 is a subtype of S1 and S2 a subtype of T2 then functions that take an argument of type S1 and return a value of type S2 are a subtype of functions that take an argument of type T1 and return a T2.

So functions are contravariant in parameter and covariant in return type.

Page 132: C++ Training Datascope Lawrence D’Antonio

Is this legal?

class A {};class B: public A {};

typedef void(*FPTR)(B*);

void foo1(B*) { std::cout << "foo1(B)\n"; }void foo2(A*) { std::cout << "foo2(A)\n"; }

void bar(FPTR g) { }

main() { bar(foo1); bar(foo2);}

Page 133: C++ Training Datascope Lawrence D’Antonio

Let’s analyze this code. Function bar() takes as its argument a function that takes a B* (and has a void return).

Function foo1() is this type of function.

So bar(foo1); should be legal.

Function foo2() takes an A* and so by the contravariant rule it should be possible to substitute foo2 for foo1 in any valid expression using foo1.

So bar(foo2); should be legal.

But…

Page 134: C++ Training Datascope Lawrence D’Antonio

The C++ compiler says that

bar(foo2); is illegal because

type6.cpp:16: error: invalid conversion from `void (*)(A*)' to `void (*)(B*)'

type6.cpp:16: error: initializing argument 1 of `void bar(void (*)(B*))'

Page 135: C++ Training Datascope Lawrence D’Antonio

But this is wrong!

It violates the contravariant rule and it violates common sense. Any argument which is valid to pass to a

void (*)(B*)

should also be valid to pass to a

void (*)(A*).

Hence, it should be legal to substitute foo2() for foo1().

Page 136: C++ Training Datascope Lawrence D’Antonio

Casts in C++

There are four different cast operators in C++

static_cast<>: handles conversions between related types.

reinterpret_cast<>: handles conversions between unrelated types.

dynamic_cast<>: performs a checked runtime conversion.

const_cast<>: throws away constness.

Page 137: C++ Training Datascope Lawrence D’Antonio

static_cast

A static cast converts between objects of related types.

For example, converting one pointer type to another, an enumeration to an integral type, or a floating-point type to an integral type.

Page 138: C++ Training Datascope Lawrence D’Antonio

Is this legal?

class A {};

class B: public A {public:void foo() { std::cout << "In foo()\n"; }

};

class C {};class D: virtual public C {};

Page 139: C++ Training Datascope Lawrence D’Antonio

Example part 2

main(){ int a = 4; float b = 6.5; int c =

static_cast<double>(a)/3;

int *p = static_cast<int*>(&b);

unsigned int *q = static_cast<unsigned int*>(&a);

Page 140: C++ Training Datascope Lawrence D’Antonio

Example part 3

const double PI = 3.14159;double d =

static_cast<double>(PI);const double cd =

static_cast<const double>(d);

double &rd = static_cast<double&>(PI);

Page 141: C++ Training Datascope Lawrence D’Antonio

Example part 4

A myA;

B myB;

A *pa = static_cast<A*>(&myB);

pa->foo();

B* pb = static_cast<B*>(&myA);

pb->foo();

Page 142: C++ Training Datascope Lawrence D’Antonio

Example part 5

C myC;

D myD;

D* pd = static_cast<D*>(&myC);

Page 143: C++ Training Datascope Lawrence D’Antonio

Let’s examine each static_cast.

int c = static_cast<double>(a)/3;

This is legal. Converts a/3 from integer into floating point division.

Note that the result then undergoes an implicit conversion back to int.

Page 144: C++ Training Datascope Lawrence D’Antonio

int *p = static_cast<int*>(&b);

This is not legal. The compiler says

error: invalid static_cast from type `float*' to type `int*'

Page 145: C++ Training Datascope Lawrence D’Antonio

unsigned int *q = static_cast<unsigned int*>(&a);

This is not legal. The compiler says

error: invalid static_cast from type `int*' to type `unsigned int*'

Page 146: C++ Training Datascope Lawrence D’Antonio

double d =static_cast<double>(PI);

This is legal. It is the same as: double d = PI;

const double cd = static_cast<const double>(d);

This is legal. It is the same as: const double cd = d;

Page 147: C++ Training Datascope Lawrence D’Antonio

double &rd = static_cast<double&>(PI);

This is not legal! The compiler says

error: invalid static_cast from type `const double' to type `double&‘

Note: it would be legal to declare

const double &rd = static_cast<const double&>(PI);

Page 148: C++ Training Datascope Lawrence D’Antonio

A *pa = static_cast<A*>(&myB);pa->foo();

The cast is legal. It’s just the usual conversion from derived class to base class.

But the function call pa->foo(); is illegal.

Class A has no member function foo().

Page 149: C++ Training Datascope Lawrence D’Antonio

B* pb = static_cast<B*>(&myA); pb->foo();

This is legal. You are allowed to cast a parent pointer to a child pointer. This is called an downcast.

The static_cast operator allows you to perform safe downcasts for non-polymorphic classes.

Note: the call pb->foo() is legal.

Page 150: C++ Training Datascope Lawrence D’Antonio

D* pd = static_cast<D*>(&myC);

Here C is a virtual base class of D. So this is illegal. The compiler says:

error: cannot convert from base `C' to derived type `D' via virtual base `C'

Page 151: C++ Training Datascope Lawrence D’Antonio

Is this legal?class A {};

class B: public A {private:

int x;public:

B(int a):x(a) {}void foo() { std::cout << "x = " << x << " in foo()\n"; }

};

B myB(5);B* pb = static_cast<B*>(&myA); pb->foo();

Page 152: C++ Training Datascope Lawrence D’Antonio

This is legal.

This is illegal. It all depends on your point of view.

The compiler accepts the code. But at run-time the following happens.

Bus error (core dumped)

This program has a type error. We are trying to print out a data member x that class A doesn’t have.

This shows that there are type errors in C++ not caught by the compiler.

Page 153: C++ Training Datascope Lawrence D’Antonio

Is this legal?class A {public:

virtual void foo() { std::cout << "In A::foo()\n"; }};

class B: public A {private: int x;public:

B(int a):x(a) {}void foo() { std::cout << "x = " << x << " in foo()\n"; }

};

B myB(5);B* pb = static_cast<B*>(&myA); pb->foo();

Page 154: C++ Training Datascope Lawrence D’Antonio

Now it’s legal. The output is

In A::foo()

Page 155: C++ Training Datascope Lawrence D’Antonio

Revised exampleclass A {public: void foo() { std::cout << "\nIn A::foo()\n"; } virtual void bar() { std::cout << "In A::bar()\n"; }};

class B: public A {private: int x;public: B(int a):x(a) {} void foo() { std::cout << "\nx = " << x << " in foo()\n" << "Address of object = " << this << '\n' << "Address of x = " << &x << '\n'; }};

Page 156: C++ Training Datascope Lawrence D’Antonio

Example 2

main(){

A myA; B myB(5); std::cout << "Size of A = " << sizeof(A) << '\n' << "Size of B = " << sizeof(B) << '\n';

myB.foo();

A *pa = static_cast<A*>(&myB); pa->foo();

Page 157: C++ Training Datascope Lawrence D’Antonio

Example 3

B* pb = static_cast<B*>(&myA);

pb->foo();

std::cout << "\nAddress of myA = "

<< &myA << '\n'

<< "Address of pb = "

<< pb << '\n';

}

Page 158: C++ Training Datascope Lawrence D’Antonio

Example 4 (output)Size of A = 4Size of B = 8

x = 5 in foo()Address of object = 0xffbff960Address of x = 0xffbff964

In A::foo()

x = 0 in foo()Address of object = 0xffbff968Address of x = 0xffbff96c

Address of myA = 0xffbff968Address of pb = 0xffbff968

Page 159: C++ Training Datascope Lawrence D’Antonio

Is this legal?

class A {public: void bar() { std::cout << "In bar()\n"; }};

class B: public A {public: void foo() { std::cout << "In foo()\n"; }};

Page 160: C++ Training Datascope Lawrence D’Antonio

Example part 2

A myA; B myB; void (B::* pmb)() = &B::foo; (myB.*pmb)(); void (A::* pma)() = &A::bar; (myA.*pma)();

Page 161: C++ Training Datascope Lawrence D’Antonio

Example part 3

pmb = &A::bar;

(myB.*pmb)();

pma = &B::foo;

(myA.*pma)();

Page 162: C++ Training Datascope Lawrence D’Antonio

void (B::* pmb)() = &B::foo;(myB.*pmb)();

This is legal. It first initializes a pointer to a member function of class B. Then the member function is called on object myB.

Page 163: C++ Training Datascope Lawrence D’Antonio

void (A::* pma)() = &A::bar;(myA.*pma)();

This is legal. It first initializes a pointer to a member function of class A. Then the member function is called on object myA.

Page 164: C++ Training Datascope Lawrence D’Antonio

pmb = &A::bar; (myB.*pmb)();

This is legal. You can safely convert a pointer to a member of the base class into a pointer to member of the derived class.

This is an instance of the contravariant rule.

Page 165: C++ Training Datascope Lawrence D’Antonio

pma = &B::foo; (myA.*pma)();

This is illegal, for good reason. You cannot convert a pointer to a member of the derived class to a pointer to a member of the base class.

Otherwise, you could call a member of the derived class from the parent.

Page 166: C++ Training Datascope Lawrence D’Antonio

Is this legal?

void (A::* pma)() =

static_cast<void (A::*)()>(&B::foo);

(myA.*pma)();

Page 167: C++ Training Datascope Lawrence D’Antonio

This is legal.

You can use static_cast to convert a pointer to a member function from one class to another class that are related by inheritance.

Page 168: C++ Training Datascope Lawrence D’Antonio

Is this legal?

class C { };

class D {public: void foo() { }};

C myC;void (C::*pmc)() = static_cast<void (C::*)()>(&D::foo);(myC.*pmc)();

Page 169: C++ Training Datascope Lawrence D’Antonio

No this is not legal.

You cannot cast a pointer to member between unrelated classes.

Page 170: C++ Training Datascope Lawrence D’Antonio

Summary of static_cast You can explicitly convert a pointer of a type A to a

pointer of a type B if A is a base class of B. If A is not a base class of B, a compiler error will result.

You may cast an lvalue of a type A to a type B& if the following are true: A is a base class of B You are able to convert a pointer of type A to a pointer

of type B The type B has the same or greater const or volatile

qualifiers than type A A is not a virtual base class of B

The result is an lvalue of type B.

Page 171: C++ Training Datascope Lawrence D’Antonio

Summary of static_cast 2

A pointer to member type can be explicitly converted into a different pointer to member type if both types are pointers to members of the same class. This form of explicit conversion may also take place if the pointer to member types are from separate classes, however one of the class types must be derived from the other.

Page 172: C++ Training Datascope Lawrence D’Antonio

Uses of static keyword

The keyword static is used to qualify a name in the following ways:

At file level: static means internal linkage. Namely, such names are local to the compilation unit.

Page 173: C++ Training Datascope Lawrence D’Antonio

Uses of static keyword 2

At the function level: static applied to a local variable in a function means a single, statically allocated object will be used to represent that variable in all calls to the function.

Page 174: C++ Training Datascope Lawrence D’Antonio

Uses of static keyword 3

At the class level: static applied to a class member means that one copy of that member is shared by all objects of that class. A static member function doesn’t need to be invoked by a particular object of that class.

Page 175: C++ Training Datascope Lawrence D’Antonio

Is this legal?

#include<iostream>

namespace { void foo()

{ std::cout << "foo\n"; }}

main() { foo(); return 0;}

Page 176: C++ Training Datascope Lawrence D’Antonio

Yes, it is legal. It is an example of an unnamed namespace.

Unnamed namespaces have an assumed using directive for the file it is defined in.

Since the namespace is unnamed, its members cannot be used in other files.

So that an unnamed namespace defines internal linkage. That means unnamed namespaces can be used in place of the keyword static at the file level.

Page 177: C++ Training Datascope Lawrence D’Antonio

reinterpret_cast

The reinterpret_cast is used to perform conversions between two unrelated types. The result of the conversion is usually implementation dependent and, therefore, not likely to be portable. You should use this type of cast only when absolutely necessary.

Page 178: C++ Training Datascope Lawrence D’Antonio

Is this legal?class A { };class B: public A {public: void foo() { std::cout << "In foo()\n"; }};

class C { };class D: virtual public C { };

class E { };class F {public: void foo() { std::cout << "In F::foo\n"; }};

Page 179: C++ Training Datascope Lawrence D’Antonio

Example part 2

main(){ int a = 4; float b = 6.5;

int *p = reinterpret_cast<int*>(&b); std::cout << *p << '\n'; unsigned int *q =

reinterpret_cast<unsigned int*>(&a); std::cout << *q << '\n';

Page 180: C++ Training Datascope Lawrence D’Antonio

Example part 3

C myC;D myD;

D* pd = reinterpret_cast<D*>(&myC);

E e;void (E::*pme)() =

reinterpret_cast<void (E::*)()>(&F::foo);(e.*pme)();

Page 181: C++ Training Datascope Lawrence D’Antonio

All of the casts are legal, since reinterpret_cast can convert one pointer type into another.

The output of the program is:

10873733124In F::foo

Page 182: C++ Training Datascope Lawrence D’Antonio

Is this legal?

What if we add the following to the previous example?

int x = reinterpret_cast<int>(myD);

Not legal. Can’t use reinterpret_cast to do arbitrary conversions.

Page 183: C++ Training Datascope Lawrence D’Antonio

Example

// Returns a hash code based on an address

unsigned short Hash( void *p ) {

unsigned int val = reinterpret_cast<unsigned int>( p );

return ( unsigned short )( val ^ (val >> 16));

}

main() {

int a[20];

for ( int i = 0; i < 20; i++ )

cout << Hash( a + i ) << endl;

}

Page 184: C++ Training Datascope Lawrence D’Antonio

Summary of reinterpret_cast

Can convert between any pointer types.

Can convert between pointer type and integral type.

Cannot cast away constness.

Page 185: C++ Training Datascope Lawrence D’Antonio

dynamic_cast

The dynamic_cast operator performs type conversions at run time.

It guarantees the conversion of a pointer to a base class to a pointer to a derived class. A program can thereby use a class hierarchy safely.

The primary purpose of dynamic_cast is to perform downcasts.

Page 186: C++ Training Datascope Lawrence D’Antonio

Is this legal?class A {public: virtual void foo() {}};

class B: public virtual A { };class C: public virtual A { };class D: public B, public C { };

main() { A *pa = new A; B *pb = new B; C *pc = new C; D *pd = new D;

pb = dynamic_cast<B*> (pa); pc = dynamic_cast<C*> (new B);

pd = dynamic_cast<D*> (pa);}

Page 187: C++ Training Datascope Lawrence D’Antonio

Yes, this is legal.

Using dynamic_cast on a polymorphic hierarchy, one can convert between pointers to classes up, down or sideways in the hierarchy.

Page 188: C++ Training Datascope Lawrence D’Antonio

Is this legal?

pb = dynamic_cast<B*> (pa); if (pb) std::cout << "Cast from A to B worked\n"; else std::cout << "Cast from A to B failed\n"; pc = dynamic_cast<C*> (new B); if (pc) std::cout << "Cast from B to C worked\n"; else std::cout << "Cast from B to C failed\n";

pd = dynamic_cast<D*>(pa); if (pd) std::cout << "Cast from A to D worked\n"; else std::cout << "Cast from A to D failed\n";

Page 189: C++ Training Datascope Lawrence D’Antonio

Yes these are all legal. But the output is:

Cast from A to B failedCast from B to C failedCast from A to D failed

Page 190: C++ Training Datascope Lawrence D’Antonio

dynamic_cast example

class A {public: virtual void foo() { }};

class B: public virtual A {private: int x;public: B(): x(0) { } B(int a): x(a) { } void bar() { std::cout << x << '\n'; }};

Page 191: C++ Training Datascope Lawrence D’Antonio

Example part 2

class C: public virtual A {};

class D: public B, public C {};

main()

{

A *pa = new D;

B *pb;

pb = dynamic_cast<B*> (pa);

if (pb) pb->bar();

else std::cout << "Cast failed\n";

Page 192: C++ Training Datascope Lawrence D’Antonio

The cast pb = dynamic_cast<B*> (pa); works because pa points at a D, which contains a B subobject.

Page 193: C++ Training Datascope Lawrence D’Antonio

RTTI

There is an operator typeid that can be used to check the types of objects.

The return type of typeid is an object of class type_info.

The == and != operators are overloaded for type_info.

One can use the member function type_info::name() to view the type name.

Page 194: C++ Training Datascope Lawrence D’Antonio

RTTI Example

class A { };class B: public A { };

main(){ A a1, a2; B *pb;

int x;

Page 195: C++ Training Datascope Lawrence D’Antonio

RTTI Example Part 2

if (typeid(a1) == typeid(a2)) std::cout << "Variable a1 is of type " << typeid(a1).name() << '\n'; std::cout << "Variable x is of type “ << typeid(x).name() << '\n'; std::cout << "Variable pb is of type " << typeid(pb).name() << '\n';

Page 196: C++ Training Datascope Lawrence D’Antonio

RTTI Example Part 3

Output from program

Variable a1 is of type 1A

Variable x is of type i

Variable pb is of type P1B

Page 197: C++ Training Datascope Lawrence D’Antonio

const_cast

Is used to add or remove const or volatile qualifiers.

Page 198: C++ Training Datascope Lawrence D’Antonio

Example

double f(double &d) { return d*d; }

void g(const double &x)

{

f(const_cast<double>(x));

}