c++ training datascope lawrence d’antonio lecture 6 an overview of c++: what is polymorphism? -...

51
C++ Training C++ Training Datascope Datascope Lawrence D’Antonio Lawrence D’Antonio Lecture 6 Lecture 6 An Overview of C++: An Overview of C++: What is Polymorphism? - What is Polymorphism? - Coercion Coercion

Upload: griselda-harvey

Post on 17-Dec-2015

216 views

Category:

Documents


2 download

TRANSCRIPT

C++ TrainingC++ TrainingDatascopeDatascope

Lawrence D’AntonioLawrence D’AntonioLecture 6Lecture 6

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

What is Polymorphism? - CoercionWhat is Polymorphism? - Coercion

What is polymorphism?What is polymorphism?

Different types of objects respond to the Different types of objects respond to the same message and use the appropriate same message and use the appropriate method.method.

Polymorphism

Universal

Ad-hoc

Parametric

Subtype

Overloading

Coercion

Coercion

A coercion is an implicit type conversion. This allows the programmer to omit a type cast.

There are three types of coercions: Compiler defined (such as promotions,

derived base) Constructor User defined

Compiler defined coercions

Simple example

double x;

x = 2; //2 promoted to double

Function call coercions

void foo(double x) { //... }

//foo() can be called with any type that can be

//converted to double

foo((short) 4);

foo(‘a’);

foo(3L);

foo(2.3F);

Is this legal?#include <iostream>

void f(char a, int b){ std::cout << a << b << '\n'; }

void f(int a, char b){ std::cout << a << b << '\n'; }

main(){ f('a','b'); return 0;}

Not legal, it’s ambiguous.

Coercion vs. Overloading example

3 + 4;

3 + 4.0;

3.0 + 4;

3.0 + 4.0;

How does this work?

Explanations of example

Four overloaded versions of operator + Two overloaded versions of operator +,

one for integers, the other for doubles. The middle two calls in the example would use coercion.

One version of operator +, for doubles. The first three calls in the example would use coercion.

Derived to Base Conversion

C++ will implicitly convert a pointer or reference to a derived class to a pointer or reference to the base class. This is because the derived class has a copy of the base class inside it.

A pointer to the base class cannot be converted to a pointer to the derived class.

Is this legal?class A {};

class B: public A {};

class C: protected A {};

class D: private A {};

main() { A *pa = new B; pa = new C; pa = new D; return 0;}

pa = new B; //Legal, B is an A

pa = new C; //Illegal, C is not an A

pa = new D; //Illegal, D is not an A

In the last two cases, the base class is inaccessible.

Is this legal?

class A {};

class C: protected A {public: void foo() { A *pa = this; }}; class D: private A {public: void foo() { A *pa = this; }}; main() { C c; c.foo();

D d; d.foo(); return 0;}

Yes, it’s legal. Protected and private inheritance exemplify a has-a relationship, rather than an is-a relationship. Member functions are allowed to convert pointer to derived into pointer to base.

Pointer to Member Conversions

What is the relationship between a pointer to member of a base class and a derived class?

Is this legal?struct A { int a;};

struct B: public A { int a;};

main(){ A a; B b; int A::*pa = &B::a; int B::*pb = &A::a;

return 0;}

Complicated.

int A::*pa = &B::a;

is illegal. Can’t convert pointer to derived member to a pointer to base member.

int B::*pb = &A::a;

Is legal. Can convert pointer to base member to a pointer to derived member.

Conversion Constructors

Constructors that take a single argument can be thought of as a type conversion.

struct X { X(int); };

So that any code that expects an object of class X can be passed an int that will then be converted to an X.

Is this legal?

struct X { X(int){} };

void f(const X &) {}void g(int) {}void g(X) {}

main() {f(3);g(4);g(X(5));

return 0;}

f(3); //Legal, you can create a temp //object X(3), which is passed to //f()

g(4); //Legal, calls g(int)

g(X(5)); //Legal, calls g(X)

Is this legal?struct X { X(int){} };

void f(X &) {}void g(int) {}void g(X) {}

main() {int a = 2;

f(3);f(a);g(4);g(X(5));

return 0;}

f(3); //Not legal, can’t use temp X object to initialize an X &

f(a); //Not legal, can’t use temp X object to initialize an X &

g(4); //Legal, as before

g(X(5)); //Legal, as before

Is this legal?

struct X { X(int){} };

struct Y { Y(X) {} };

void f(Y) {}

main() {

f(3);

return 0;

}

Not legal.

Basically, only one level conversions are allowed. Not allowed to convert int to X to Y.

Is this legal?struct X { X(int){} };

void f(X) {}void g(int) {}void g(X) {}

main() {int a = 2;

f(3);f(a);g(4);g(X(5));

return 0;}

f(3); //Legal, temp X is passed by value

f(a); //Legal, temp X is passed by value

g(4); //Legal, as before

g(X(5)); //Legal, as before

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

class Y { X ex;public: Y(X x): ex(x) { std::cout << "World\n"; } void foo() { ex.foo(); } void foo(X x) { x.foo(); } void foo(Y y) { y.foo(); }};

main() {Y y(X());y.foo(y); return 0;

}

Not legal! The problem is y.foo(y).

foo is not a member of y, which is of non-class type Y ()(X (*)()).

This can be fixed with Y y = X();

Explicit Constructors

Single argument constructors (or constructors that can be called with a single argument) can lead to unexpected consequences.

class X {

int x;

public:

X(int a = 0):x(a) {}

};

main() {

X ptr_x = 0;

//...

That was bad!

The programmer probably meant

X *ptr_x = 0;

But the compiler performed the conversion

X ptr_x = X(0);

Solution

class X {int x;

public:explicit X(int a = 0):x(a) {}

};

main() {X ptr_x = 0;

//...

The compiler will flag the following as an error:

X ptr_x = 0;

This code implicitly uses the constructor X(int)

In order to use this constructor, one must explicitly call the constructor.

X ptr_x = X(0);

Constructor Rule

Any single argument constructor should be declared explicit.

Conversion operators

A user defined operator that converts a user defined object into another type.

The destination type may be a built-in or user defined type.

Example

class Fraction {

int numerator, denominator;

public:

Fraction(int n = 0, int d = 1): numerator(n),

denominator(d) {}

//...

operator double() const { return

static_cast<double>(numerator)/denominator;

}

};

Usage

Fraction f(3,4);

double d = f; // d is 0.75

double x = f + 2;

Is this legal?

Fraction f(1,2), g(1,3);

Fraction h;

h = f + g;

Hard to tell!

h = f + g;

This is legal if there is an overloaded operator + that takes Fraction operands.

This is also legal if, in the unlikely case, there was a ctor Fraction(double).

Perils of conversiontemplate <class T>class Ptr {

T *p;public://...

operator bool() const {return p ? true : false;

}};

Ptr<Apple> ptr1;Ptr<Orange> ptr2;

if (ptr1 == ptr2) {/...}

The comparison ptr1 == ptr2 is valid because the Ptrs are converted into bools and then compared. So Apples and Oranges can be compared!

If operator == were overloaded for type Ptr, then that version would be called.

Is this legal?

string filename = “data.txt”;

ifstream in_file(filename);

Not legal!

ifstream constructor expects a

const char * argument.

Legal version:

ifstream in_file(filename.c_str());

Is this legal?

//Let’s define a conversion operator for//string class

operator const char*(const string &s) {

return s.c_str();}

string filename = “data.txt”;ifstream in_file(filename);

Not legal!

Conversion operators can only be member functions.

C++ is weakly typed

class Tupid {

public:

template <class X>

operator X {

return X();

}

};

This means that objects of class T can be converted into an object of any (yes, any) type that has a default constructor.

Tupid t;

int a = t;

string s = t;

vector<double> v = t;

Breaking encapsulationclass A { public:

void foo() {}; } ;

class D: private A {public: A *foo() { A *pa = this; return pa; }};

main() { D d; A *pa = d.foo(); pa->foo(); //Calls A::foo//...

Breaking encapsulation 2

class A { int a;public: int *foo() { return &a; }};

main() { A a;

int *p = a.foo(); *p = 12;

Breaking encapsulation 3class A {private: int a;};

class B: public A {};

main(){ A a; B b; int B::*pb = &A::a;

b.*pb = 5;

return 0;}

Not legal. A::a accesses private data