c++ training datascope lawrence d’antonio lecture 6 an overview of c++: what is polymorphism? -...
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
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;}
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){} };
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.
That was bad!
The programmer probably meant
X *ptr_x = 0;
But the compiler performed the conversion
X ptr_x = 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);
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;
}
};
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.
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);
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;}