C++ Training Datascope Lawrence D’Antonio

Download Report

Transcript C++ Training Datascope Lawrence D’Antonio

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
 The largest allowed range of values for a
program variable is its type.
 Languages in which variables have nontrivial types are called typed languages.
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).
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.
Type Systems 4
 Languages in which programs are safe are
called safe languages.
Typed
Untyped
Safe
ML, Java
LISP
Unsafe
C
Assembler
Types 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.
Types of typing 2
 Soft typing: Type checking occurs, but
incorrect types are only given warnings.
 Manifest typing: Types are named
explicitly in the code.
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
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
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
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.
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.
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.
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.
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.
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.
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.
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.
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 };
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)
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.
Type inference 5
 C++ uses type inference.
 For example, template parameters are
deduced from the argument types in a
function call.
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;
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.
Type inference 8
 Cobra example
class Foo
def bar
i = 1_000_000
for j = 0 .. i
doSomething(j)
def doSomething(i as int)
pass
Type inference 9
 F# example
let 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()
}
Type inference 10
 Nemerle example
def d = Dictionary ();
d.Add ("Ala", 7);
foreach (s in args) { ... }
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.
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.
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.
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.
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.
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.
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.
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.
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 highlevel programs into machine code.
 The compiler-compiler yacc is a
metaprogramming tool to generate a tool
for translating high level programs into
machine code.
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~
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
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");
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. */
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"))
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. */
}
Metaprogramming 12
 An extreme example of metaprogramming
is language-oriented programming.
 To solve a problem using languageoriented programming, one doesn’t use a
general-purpose language.
 Instead the programmer creates a domain
specific programming language.
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.
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
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"
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(",")
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
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
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
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>
Ruby Example 7
 Attributes are first-class citizens
person = 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
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)
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.
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.
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.
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.
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.
Is this legal?
main()
{ //Converts float to string of 4 chars
unsigned char *c;
float f = 10;
for (c = (char *)&f;
c < sizeof(float) + (char *)&f; c++) {
std::cout << *c;
}
std::cout<< ‘\n’;
return 0;
}
Answer part 1
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 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.
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.
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.
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; }
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
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.
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.
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
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,
Duck typing 4
 How would you do this example in C++?
 Would you use inheritance or templates?
Duck typing 5
template<class T, class U>
T calculate(const T &a, const T &b,
const U &c)
{
return (a+b)*c;
}
Weak typing example
var x := 5;
var y := "37";
Print(x + y);
In Visual Basic this prints: 42
In JavaScript this prints: 537
Type and Class Hierarchies
 There are four kinds of inheritance:
 Substitution inheritance
 Inclusion inheritance
 Constraint inheritance
 Specialization inheritance
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.
Substitution inheritance 2
 This means that objects of type t extend
objects of type t'.
Open-Closed Principle
 From Bertran Meyer,
 “Software entities (classes, modules,
functions, etc.) should be open for
extension, but closed for modification.”
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.
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.
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();
}
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).
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.”
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.
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;
};
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.
LSP 5
class 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);
}
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
LSP 7
 But what happens with the following code?
void f(Rectangle& r)
{
r.SetWidth(32);
}
//...
Square s;
s.SetHeight(3);
f(s);
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!
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).
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.
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;
};
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.
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.
LSP 14
void g(Rectangle& r)
{
r.SetWidth(5);
r.SetHeight(4);
assert(r.GetWidth() * r.GetHeight()
== 20);
}
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?
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().
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.
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.
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.
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.
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.”
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.
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.
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).
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.
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.
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.
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; }
};
Type vs. Class 4
class 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; }
};
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;
}
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.
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.
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.
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.
Type vs. Class 10
 In STL, a fundamental concept is that of a
Container.
 Sequence Container and
Associative Container are types of
Container.
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.
Is this legal?
class A {};
class B: public A {};
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; }
};
Example part 3
main() {
C *p = new C;
p->foo();
p = new D;
p->foo();
return 0;
}
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++.
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(); }
};
Not legal!
invalid covariant return type
for `virtual A D::foo()'
overriding `virtual B C::foo()'
Again, incorrect return type for overriding
function.
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].
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.
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(); }
};
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.
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(); }
};
Yes this is legal.
This is a valid use of covariant return types.
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.
Contravariant types 2
 The general relationship between functions
and types can be expressed:
 If T1  S1 and S2  T2 then
S1  S2  T1  T2
 This is called the contravariant rule.
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.
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);
}
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…
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*))'
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().
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.
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.
Is this legal?
class A {};
class B: public A {
public:
void foo()
{ std::cout << "In foo()\n"; }
};
class C {};
class D: virtual public C {};
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);
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);
Example part 4
A myA;
B myB;
A *pa = static_cast<A*>(&myB);
pa->foo();
B* pb = static_cast<B*>(&myA);
pb->foo();
Example part 5
C myC;
D myD;
D* pd = static_cast<D*>(&myC);
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.
int *p = static_cast<int*>(&b);
This is not legal. The compiler says
error: invalid static_cast from type `float*' to
type `int*'
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*'
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;
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);
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().
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.
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'
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();
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.
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();
Now it’s legal. The output is
In A::foo()
Revised example
class 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'; }
};
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();
Example 3
B* pb = static_cast<B*>(&myA);
pb->foo();
std::cout << "\nAddress of myA = "
<< &myA << '\n'
<< "Address of pb = "
<< pb << '\n';
}
Example 4 (output)
Size of A = 4
Size of B = 8
x = 5 in foo()
Address of object = 0xffbff960
Address of x = 0xffbff964
In A::foo()
x = 0 in foo()
Address of object = 0xffbff968
Address of x = 0xffbff96c
Address of myA = 0xffbff968
Address of pb = 0xffbff968
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"; }
};
Example part 2
A myA;
B myB;
void (B::* pmb)() = &B::foo;
(myB.*pmb)();
void (A::* pma)() = &A::bar;
(myA.*pma)();
Example part 3
pmb = &A::bar;
(myB.*pmb)();
pma = &B::foo;
(myA.*pma)();
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.
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.
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.
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.
Is this legal?
void (A::* pma)() =
static_cast<void (A::*)()>(&B::foo);
(myA.*pma)();
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.
Is this legal?
class C { };
class D {
public:
void foo() { }
};
C myC;
void (C::*pmc)() = static_cast<void (C::*)()>(&D::foo);
(myC.*pmc)();
No this is not legal.
You cannot cast a pointer to member
between unrelated classes.
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.
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.
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.
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.
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.
Is this legal?
#include<iostream>
namespace {
void foo()
{ std::cout << "foo\n"; }
}
main() {
foo();
return 0;
}
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.
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.
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"; }
};
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';
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)();
All of the casts are legal, since reinterpret_cast
can convert one pointer type into another.
The output of the program is:
1087373312
4
In F::foo
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.
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;
}
Summary of reinterpret_cast
 Can convert between any pointer types.
 Can convert between pointer type and
integral type.
 Cannot cast away constness.
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.
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;
C *pc = new C;
B *pb = new B;
D *pd = new D;
pb = dynamic_cast<B*> (pa);
pc = dynamic_cast<C*> (new B);
pd = dynamic_cast<D*> (pa);
}
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.
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";
Yes these are all legal. But the output is:
Cast from A to B failed
Cast from B to C failed
Cast from A to D failed
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'; }
};
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";
The cast pb = dynamic_cast<B*> (pa);
works because pa points at a D, which
contains a B subobject.
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.
RTTI Example
class A { };
class B: public A { };
main()
{
A a1, a2;
B *pb;
int x;
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';
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
const_cast
 Is used to add or remove const or volatile
qualifiers.
Example
double f(double &d) { return d*d; }
void g(const double &x)
{
f(const_cast<double>(x));
}