C training datascope lawrence d antonio
This presentation is the property of its rightful owner.
Sponsored Links
1 / 198

C++ Training Datascope Lawrence D’Antonio PowerPoint PPT Presentation


  • 38 Views
  • Uploaded on
  • Presentation posted in: General

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

Download Presentation

C++ Training Datascope Lawrence D’Antonio

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


C training datascope lawrence d antonio

C++ TrainingDatascopeLawrence D’Antonio

Lecture 9

An Overview of C++:

What is Typing?


Type systems

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.


Type systems 2

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

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

Type Systems 4

Languages in which programs are safe are called safe languages.


Types of typing

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

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

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 systems1

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 21

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

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

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

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

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

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

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

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

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

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

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

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

Type inference 5

  • C++ uses type inference.

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


Type inference 6

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

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

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

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

Type inference 10

  • Nemerle example

    def d = Dictionary ();

    d.Add ("Ala", 7);

    foreach (s in args) { ... }


Dynamic typing

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

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

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

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

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

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

  • 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

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

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.


Metaprogramming 6

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

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

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

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

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

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

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.


Metaprogramming 13

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 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Duck typing 4

  • How would you do this example in C++?

  • Would you use inheritance or templates?


Duck typing 5

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

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

Type and Class Hierarchies

  • There are four kinds of inheritance:

  • Substitution inheritance

  • Inclusion inheritance

  • Constraint inheritance

  • Specialization inheritance


Substitution 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

Substitution inheritance 2

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


Open closed principle

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-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

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

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

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).


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.”


Lsp 2

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

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

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

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

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

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

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

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

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

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

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

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

LSP 14

void g(Rectangle& r)

{

r.SetWidth(5);

r.SetHeight(4);

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

== 20);

}


Lsp 15

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 Rectangleleaves its height unchanged?


Lsp 16

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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 legal1

Is this legal?

class A {};

class B: public A {};


Example part 2

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

Example part 3

main() {

C *p = new C;

p->foo();

p = new D;

p->foo();

return 0;

}


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++.


Is this legal2

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(); }

};


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.


Covariant types

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

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 legal3

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(); }

};


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.


Is this legal4

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(); }

};


C training datascope lawrence d antonio

Yes this is legal.

This is a valid use of covariant return types.


Contravariant 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

Contravariant types 2

  • The general relationship between functions and types can be expressed:

  • If and then

  • This is called the contravariant rule.


Contravariant types 3

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 legal5

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);

}


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…


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*))'


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().


Casts in c

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

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 legal6

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 21

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 31

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

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

Example part 5

C myC;

D myD;

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


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.


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*'


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*'


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;


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);


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().


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.


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'


Is this legal7

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();


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.


Is this legal8

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();


C training datascope lawrence d antonio

Now it’s legal. The output is

In A::foo()


Revised example

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

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

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

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 legal9

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 22

Example part 2

A myA;

B myB;

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

(myB.*pmb)();

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

(myA.*pma)();


Example part 32

Example part 3

pmb = &A::bar;

(myB.*pmb)();

pma = &B::foo;

(myA.*pma)();


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.


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.


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.


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.


Is this legal10

Is this legal?

void (A::* pma)() =

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

(myA.*pma)();


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.


Is this legal11

Is this legal?

class C { };

class D {

public:

void foo() { }

};

C myC;

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

(myC.*pmc)();


C training datascope lawrence d antonio

No this is not legal.

You cannot cast a pointer to member between unrelated classes.


Summary of static cast

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

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

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

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

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 legal12

Is this legal?

#include<iostream>

namespace {

void foo()

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

}

main() {

foo();

return 0;

}


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.


Reinterpret cast

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 legal13

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 23

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 33

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)();


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:

1087373312

4

In F::foo


Is this legal14

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

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

Summary of reinterpret_cast

  • Can convert between any pointer types.

  • Can convert between pointer type and integral type.

  • Cannot cast away constness.


Dynamic cast

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 legal15

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);

}


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.


Is this legal16

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";


C training datascope lawrence d antonio

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

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 24

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";


C training datascope lawrence d antonio

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


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.


Rtti example

RTTI Example

class A { };

class B: public A { };

main()

{

A a1, a2;

B *pb;

int x;


Rtti example part 2

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

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

const_cast

  • Is used to add or remove const or volatile qualifiers.


Example1

Example

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

void g(const double &x)

{

f(const_cast<double>(x));

}


  • Login