Presentation is loading. Please wait.

Presentation is loading. Please wait.

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

Similar presentations


Presentation on theme: "C++ Training Datascope Lawrence D’Antonio Lecture 9 An Overview of C++: What is Typing?"— Presentation transcript:

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

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

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

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

5 Type Systems 4   Languages in which programs are safe are called safe languages. TypedUntyped SafeML, JavaLISP UnsafeCAssembler

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

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

8 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

9 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

10 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

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

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

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

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

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

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

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

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

19 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 };

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

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

22 Type inference 5   C++ uses type inference.   For example, template parameters are deduced from the argument types in a function call.

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

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

25 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

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

27 Type inference 10   Nemerle example def d = Dictionary (); d.Add ("Ala", 7); foreach (s in args) {... }

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

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

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

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

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

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

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

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

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

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

38 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

39 Metaprogramming 8   Quine example in C #include 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");

40 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. */

41 Metaprogramming 10 B(printf("#include \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"))

42 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. */ }

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

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

45 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

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

47 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(",")

48 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

49 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

50 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

51 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: #

52 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

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

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

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

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

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

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

59 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; }

60 No this is not considered legal by the compiler. There is a type error. type.cpp:8: error: invalid conversion from `char*' to `unsigned char*' type.cpp:8: error: comparison between distinct pointer types `unsigned char*' and `char*' lacks a cast The compiler is complaining about the comparison: c < sizeof(float) + (char *)&f Answer part 1

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

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

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

64 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; }

65 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

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

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

68 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

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

70 Duck typing 4   How would you do this example in C++?   Would you use inheritance or templates?

71 Duck typing 5 template T calculate(const T &a, const T &b, const U &c) { return (a+b)*c; }

72 Weak typing example var x := 5; var y := "37"; Print(x + y); In Visual Basic this prints: 42 In JavaScript this prints: 537

73 Type and Class Hierarchies   There are four kinds of inheritance:   Substitution inheritance   Inclusion inheritance   Constraint inheritance   Specialization inheritance

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

75 Substitution inheritance 2   This means that objects of type t extend objects of type t'.

76 Open-Closed Principle   From Bertran Meyer,   “Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.”

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

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

79 Open-Closed Principle 4   Suppose we have a Shape class hierarchy. Let us define a function: void DrawAllShapes(set &s) { std::set ::iterator p; for(p = s.begin(); p != s.end(); p++) (*p)->draw(); }

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

81 LSP   The Liskov Substitution Principle states   “If for each object o 1 of type S there is an object o 2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o 1 is substituted for o 2 then S is a subtype of T.”

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

83 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; };

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

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

86 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

87 LSP 7   But what happens with the following code? void f(Rectangle& r) { r.SetWidth(32); } //... Square s; s.SetHeight(3); f(s);

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

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

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

91 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; };

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

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

94 LSP 14 void g(Rectangle& r) { r.SetWidth(5); r.SetHeight(4); assert(r.GetWidth() * r.GetHeight() == 20); }

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

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

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

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

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

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

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

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

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

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

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

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

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

108 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; } };

109 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; } };

110 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; }

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

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

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

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

115 Type vs. Class 10   In STL, a fundamental concept is that of a Container.   Sequence Container and Associative Container are types of Container.

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

117 Is this legal? class A {}; class B: public A {};

118 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; } };

119 Example part 3 main() { C *p = new C; p->foo(); p = new D; p->foo(); return 0; }

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

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

122 Not legal! invalid covariant return type for `virtual A D::foo()' overriding `virtual B C::foo()' Again, incorrect return type for overriding function.

123 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].

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

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

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

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

128 Yes this is legal. This is a valid use of covariant return types.

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

130 Contravariant types 2   The general relationship between functions and types can be expressed:   If and then   This is called the contravariant rule.

131 Contravariant types 3   This rules states that if T 1 is a subtype of S 1 and S 2 a subtype of T 2 then functions that take an argument of type S 1 and return a value of type S 2 are a subtype of functions that take an argument of type T 1 and return a T 2.   So functions are contravariant in parameter and covariant in return type.

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

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

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

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

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

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

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

139 Example part 2 main() { int a = 4; float b = 6.5; int c = static_cast (a)/3; int *p = static_cast (&b); unsigned int *q = static_cast (&a);

140 Example part 3 const double PI = 3.14159; double d = static_cast (PI); const double cd = static_cast (d); double &rd = static_cast (PI);

141 Example part 4 A myA; B myB; A *pa = static_cast (&myB); pa->foo(); B* pb = static_cast (&myA); pb->foo();

142 Example part 5 C myC; D myD; D* pd = static_cast (&myC);

143 Let’s examine each static_cast. int c = static_cast (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.

144 int *p = static_cast (&b); This is not legal. The compiler says error: invalid static_cast from type `float*' to type `int*'

145 unsigned int *q = static_cast (&a); This is not legal. The compiler says error: invalid static_cast from type `int*' to type `unsigned int*'

146 double d = static_cast (PI); This is legal. It is the same as: double d = PI; const double cd = static_cast (d); This is legal. It is the same as: const double cd = d;

147 double &rd = static_cast (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 (PI);

148 A *pa = static_cast (&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().

149 B* pb = static_cast (&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.

150 D* pd = static_cast (&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'

151 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 (&myA); pb->foo();

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

153 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 (&myA); pb->foo();

154 Now it’s legal. The output is In A::foo()

155 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'; } };

156 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 (&myB); pa->foo();

157 Example 3 B* pb = static_cast (&myA); pb->foo(); std::cout << "\nAddress of myA = " << &myA << '\n' << "Address of pb = " << pb << '\n'; }

158 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

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

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

161 Example part 3 pmb = &A::bar; (myB.*pmb)(); pma = &B::foo; (myA.*pma)();

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

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

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

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

166 Is this legal? void (A::* pma)() = static_cast (&B::foo); (myA.*pma)();

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

168 Is this legal? class C { }; class D { public: void foo() { } }; C myC; void (C::*pmc)() = static_cast (&D::foo); (myC.*pmc)();

169 No this is not legal. You cannot cast a pointer to member between unrelated classes.

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

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

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

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

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

175 Is this legal? #include namespace { void foo() { std::cout << "foo\n"; } } main() { foo(); return 0; }

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

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

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

179 Example part 2 main() { int a = 4; float b = 6.5; int *p = reinterpret_cast (&b); std::cout << *p << '\n'; unsigned int *q = reinterpret_cast (&a); std::cout << *q << '\n';

180 Example part 3 C myC; D myD; D* pd = reinterpret_cast (&myC); E e; void (E::*pme)() = reinterpret_cast (&F::foo); (e.*pme)();

181 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

182 Is this legal?   What if we add the following to the previous example? int x = reinterpret_cast (myD);   Not legal. Can’t use reinterpret_cast to do arbitrary conversions.

183 Example // Returns a hash code based on an address unsigned short Hash( void *p ) { unsigned int val = reinterpret_cast ( p ); return ( unsigned short )( val ^ (val >> 16)); } main() { int a[20]; for ( int i = 0; i < 20; i++ ) cout << Hash( a + i ) << endl; }

184 Summary of reinterpret_cast   Can convert between any pointer types.   Can convert between pointer type and integral type.   Cannot cast away constness.

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

186 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 (pa); pc = dynamic_cast (new B); pd = dynamic_cast (pa); }

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

188 Is this legal? pb = dynamic_cast (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 (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 (pa); if (pd) std::cout << "Cast from A to D worked\n"; else std::cout << "Cast from A to D failed\n";

189 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

190 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'; } };

191 Example part 2 class C: public virtual A {}; class D: public B, public C {}; main() { A *pa = new D; B *pb; pb = dynamic_cast (pa); if (pb) pb->bar(); else std::cout << "Cast failed\n";

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

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

194 RTTI Example class A { }; class B: public A { }; main() { A a1, a2; B *pb; int x;

195 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';

196 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

197 const_cast   Is used to add or remove const or volatile qualifiers.

198 Example double f(double &d) { return d*d; } void g(const double &x) { f(const_cast (x)); }


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

Similar presentations


Ads by Google