COMP2006 Lecture 12 Casting and Operator Overloading
COMP2006 Lecture 12 Casting and Operator Overloading
C++ Programming
Lecture 12
Dr Chao Chen
1
This Lecture
• Casting
– static cast
– dynamic cast
– const cast
– reinterpret cast
• Operator overloading
• Questions
2
Breaking the rules
Casting
3
Unchangable values?
• Here we have constant references passed in
• Q1: Can we use x and y to change the variable values?
void foo(
const int& x,
const int& y )
{
x = 5;
y = 19;
}
void static_cast_example()
{
float f = 4.1;
// Convert float to an int
int i = static_cast<int>(f);
printf( "f = %f, i = %d\n", f, i );
} 9
dynamic_cast <type> (var)
• Casting from derived class to base class is easy
– Derived class object IS a base class object
– Base class pointer might not point at a derived class object
• dynamic_cast<>()
– Safely convert from a base-class pointer or reference to a sub-
class pointer or reference
– Checks the type at run-time rather than compile-time
– Returns NULL if the type conversion of a pointer cannot take
place (i.e. it is not of the target type)
– There is no such thing as a NULL reference
If reference conversion fails, it throws an exception of type
std::bad_cast
• Note: We will come to exceptions later – think of them as
similar to the Java exceptions for the moment
10
static_cast example
sub1 s1;
base
sub1* ps1 = &s1;
sub1 sub2
// Fine: treat as base class
base* pb1 = ps1;
// Treat as sub-class
sub2* ps2err = static_cast<sub2*>(pb1);
// Static cast: do conversion.
ps2err->func();
// This is a BAD practice.
// Treating sub1 object as a sub2 object
11
dynamic_cast example
sub1 s1;
sub1* ps1 = &s1; base
// Treat as sub-class
sub2* ps2safe = dynamic_cast<sub2*>(pb1);
// Dynamic cast: runtime check
if ( ps2safe == NULL )
printf( "Dynamic cast on pb2 failed\n" );
else
ps2safe->func();
12
Exception thrown by dynamic_cast
void foo()
{ Dynamic cast on a reference
Sub1 s1;
Base& rb = s1;
Sub2& rs2 = dynamic_cast<Sub2&>(rb);
cout << "No exception was thrown by foo()" << endl;
}
int main() class Base
{
try
{
foo(); class Sub1 class Sub2
}
catch ( bad_cast )
{ cout << "bad_cast exception thrown" << endl; }
catch ( ... )
{ cout << "Other exception thrown" << endl; }
} 13
Note: s1 is destroyed properly when stack frame is destroyed
reinterpret_cast<type>(var)
• reinterpret_cast<>()
– Treat the value as if it was a different type
– Interpret the bits in one type as another
– Including platform dependent conversions
– Hardly ever needed, apart from with low-level code
– Like saying “Trust me, you can treat it as one of these”
– e.g.:
void reinterpret_cast_example()
{
int i = 1;
int* p = & i;
i = reinterpret_cast<long>(p);
printf( "i = %x, p = %p\n", i, p );
} 14
A Casting Question
• Where are casts needed, and what sort of
casts should be used?
(Assume BouncingBall is a sub-class of BaseEngine)
BouncingBall game;
BaseEngine* pGame = &game; // ?
BouncingBall* pmGame = pGame; // ?
BouncingBall game;
BaseEngine& rgame = game; // ?
BouncingBall& rmgame = rgame; // ?
15
Answer : pointers
BouncingBall game;
BaseEngine* pGame = &game; // No cast
BouncingBall* pmGame =
dynamic_cast<BouncingBall*>(pGame);
if ( pGame==NULL ) { /* Failed */ }
18
Examples
#include <string>
#include <iostream>
int main()
{
string s1( "Test string" );
int i = 1; Overloaded operator - input
private:
float f;
Internal string and float
string strName;
}; 21
Printing – member functions
// Constructor
MyFloat( const char* szName, float f )
: f(f), strName(szName)
{}
Main function:
MyFloat f1("f1", 1.1f);
f1 : 1.1
f1.print();
f2 : 3.3
MyFloat f2("f2", 3.3f);
f2.print();
22
Non-member operator overload
MyFloat operator-( const MyFloat& lhs, const MyFloat& rhs )
{
MyFloat temp(
lhs.strName + "-" + rhs.strName, /* strName */
lhs.f - rhs.f); /* f, float value */
return temp;
}
class MyFloat
{
…
// Non-member operator overload – friend can access private
friend MyFloat operator-( const MyFloat& lhs,
const MyFloat& rhs );
…
23
}
Non-member operator overload
MyFloat operator-( const MyFloat& lhs, const MyFloat& rhs )
{
MyFloat temp( Calls the two-parameter constructor
lhs.strName + "-" + rhs.strName, /* strName */
lhs.f - rhs.f); /* f, float value */
return temp;
Uses + on the strings
}
class MyFloat
Needs to be a friend in this case, to
{
access the private parts (no ‘getters’)
…
// Non-member operator overload – friend can access private
friend MyFloat operator-( const MyFloat& lhs,
const MyFloat& rhs );
…
24
}
Non-member operator overload
MyFloat operator-( const MyFloat& lhs, const MyFloat& rhs )
{
MyFloat temp(
lhs.strName + "-" + rhs.strName, /* strName */
lhs.f - rhs.f); /* f, float value */
return temp;
}
MyFloat f3 = f1 - f2;
Output: f1-f2 : -2.2
f3.print();
25
Or simplified version…
MyFloat operator-( const MyFloat& lhs, const MyFloat& rhs )
{
return MyFloat( lhs.strName + "-" + rhs.strName,
lhs.f - rhs.f );
}
MyFloat f3 = f1 - f2;
Output: f1-f2 : -2.2
f3.print();
26
Member function version
MyFloat MyFloat::operator + ( const MyFloat& rhs ) const
{
return MyFloat( this->strName + "+" + rhs.strName,
this->f + rhs.f );
}
// Non-member function
MyFloat operator- ( const MyFloat& lhs,
const MyFloat& rhs )
// Non-member function
friend MyFloat operator- (const MyFloat& lhs,
const MyFloat& rhs )
31
Key tricky concepts
1. = is not the same as ==
– Assignment operator = is created by default
– Equivalence operation == is NOT
2. How do we supply implementations for
both ob++ and ++ob?
3. != and == are different
– Although you could return ! (x==y) for the !=
4. + and += are different
– Similarly for other similar operators
32
== vs = operators
class C
• The code on the left will NOT
{ compile:
public:
C( int v1=1, int v2=2 ) g++ file.cpp
: i1(v1), i2(v2)
{} In function `int main()':
file.cpp:17: error: no
int i1, i2; match for 'operator=='
}; in 'c1 == c2'
bool MyClass::operator!=
(const MyClass &other) const
{
return !(*this == other);
}
36
Operator overloading summary
• Can define/change meaning of an operator, e.g.:
MyFlt operator-(const MyFlt&, const MyFlt&);
• You can make the functions member functions
MyFlt MyFlt::operator-(const MyFlt& rhs) const;
– Left hand side is then the object it is acting upon
• Act like any other function, only syntax is different:
– Converts a-b to a.operator-(b) or operator-(a,b)
• Access rights like any other function
– e.g. has to be a friend or member to access private/protected
member data/functions
38
Earlier example, again
#include <string> Cin, cout and cerr are objects
#include <iostream> extern istream cin;
extern ostream cout;
using namespace std; extern ostream cerr;
int main()
{ >> is implemented for the istream
string s1( "Test string" ); class for each type of value on the
int i = 1; left-hand side of the operator
42
Operator overloading - what to know
• Know that you can change the meaning of
operators
• Know that operator overloading is available
as both a member function version and a
global (non-member) function version
• Be able to provide the code for the
overloading of an operator
– Parameter types, const?
– Return type
– Return value? (*this) or a new stack object?
– Simple implementations
43
Questions to ask yourself
• Define as a member or as a global?
– If global then does it need to be a friend?
• What should the parameter types be?
– References?
– Make them const if you can
– If member, should the function be const?
• What should the return type be?
– Should it return *this and return reference?
– Does it need to return a copy of the object?
• e.g. post-increment must return a copy
44
Next lecture
• Template functions
– And how to fully replace macros
– Using inline functions too
– Think ‘Java Generics’, <>
• Template classes
– How standard template library works
45