C++ Features and Constructs Ch. 3 except 3.2, 3.4, 3.9, 3.11
Scope Programs are built of nested "scopes". –Sometimes called "scope blocks" or "name spaces" Inner scopes "hide" or "shadow" outer scopes. Variables are only accessible when they are "in scope".
Kinds of scope Three main kinds of scope in C++ –Local: function or block –Class: extent of the class –File: only available within a file Also –Function scope: labels and gotos –Global scope
Scope Examples long x; float y; int z; void fn(char c, int x) {// parameter hides global x extern int z;// refer to global z double y = 3.14;// shadows global y { char y;// shadows double y from above y = c;// assigns to inner y ::y = 0.3;// assign to global y } y = y/3.0;// assign to local y z++;// increment global z }
Class Scope Class members and methods are available anywhere in the class without forward declaration (a.k.a. prototype) Examples: Vector::Vector(int x, int y) { Vector::x = x; Vector::y = y; }
New Topic: Optional Arguments Arguments to a function can be given default values. Example: int echo(int value=0) { return value; } echo(10); // returns 10 echo(); // returns 0
Optional Arguments (cont’d) Optional arguments must be at the end of the argument list When might this be useful?
New Topic: Overloading Functions Functions have signatures –A function's signature is the function's name its parameter list (types and orders) C++ allows functions with different signatures to have different definitions.
Overloading Functions Examples: int power(int a, int n); int power(double a, int n); What's a problem here?
Function Call Resolution: 1. If there's an exact match, call that version 2. Match through standard type promotions 3. Match through standard conversions 4. Match through user supplied conversions (section 8.7) 5. No match (error)
C++ is Strongly Typed. C++ is strongly typed –All variables must have a type before being used. Types must match before function calls can be allowed
Standard Promotions Promotions take types and increase their resolution Examples: –short to int –int to long –...
Standard conversions Conversions convert a variable to a different type Examples: –int to float –float to int –...
Example What happens in the following fragment: float x, a; int y; a = x + y;
C++ is Strongly Typed Types still need to match –Consider two functions like: int operator +(int lhs, int rhs); float operator +(float lhs, float rhs); –One of these must match for a = x + y;
When Does Conversion Happen? When does the compiler do this? –Arithmetic –Function calls (argument type conversion) –Return values
Explicit Type Conversion (Type Casting) In C++, casts look like: ( ) int x = int(3.14);
New Topic: More About References References can be variables as well as parameters to functions They must be initialized when declared Example: –int a; –int &ra = a;// ra is a reference to a
References (cont’d) A reference must be initialized to an "lvalue" –an lvalue is anything that can appear on the left- hand side of an assignment statement
New Topic: Read-only Variables (const) Variables can be declared read-only Example: const = ; const int forever_one = 1; Read-only variables are initialized at declaration but cannot be changed at run- time.
Read-only Parameters (const) Parameters can be read-only too. Example: int foo(const int x) {... } How does this come in handy?
New Topic: Dynamic Allocation in C++ Use operator new in place of malloc() = malloc( ); = new ; new understands about class constructors!
new [] new can be used for arrays too Example: –int * array = new int [10];
delete vs. free free( ); delete ;
Dynamic Allocation Examples int * int_ptr = new int; int * ten_ptr = new int(10); int [] int_array = new int [10]; Vector * vptr = new Vector(5, 6); // calls vector constructor! delete int_ptr; delete ten_ptr; delete [] int_prt; delete vptr;
A Longer Example: Fractions Fractions are numbers of the form: numerator/denominator Fractions have several properties: –The numerator and denominator are both integers. –The denominator cannot be 0 –Arithmetic operations are well defined for fractions.
Questions about Fractions How should a fraction be stored? –A pair of integers, numerator and denominator What operations should we support for fractions? –+, -, - (unary), etc. –, == –reduce(), print()
class Fraction { public: Fraction(); Fraction(int); Fraction(int, int); print(); Fraction operator +(Fraction); Fraction operator -(); Fraction operator -(Fraction); int operator <(Fraction); int operator >(Fraction); int operator ==(Fraction); private: int numerator, denominator; };
Constructor Considerations What if denominator is zero? –Exit the program We have three cases: –no arguments (default to 0/1) –one argument (numerator) –two arguments (numerator and denominator) Anything else?
Constructors Fraction::Fraction() { numerator = 0; denominator = 0; } Fraction::Fraction(int num) { numerator = num; denominator = 1; } Fraction::Fraction(int num, int denom) { numerator = num; if (denom == 0) exit(1); denominator = denom; }
print() Print() should print the fraction in the form numerator/denominator to stdout void print() { cout << numerator << “/” << denominator; }
Fraction Fraction::operator +(Fraction right) { Fraction answer; int left_num, right_num; left_num = right.denominator * numerator; right_num = denominator * right.numerator; answer.denominator = right.denominator * denominator; answer.numerator = left_num + right_num; return answer; }
Fraction Fraction::operator -() { Fraction answer(-numerator, denominator); return answer; }
Fraction Fraction::operator -(Fraction right) { Fraction answer; int left_num, right_num; left_num = right.denominator * numerator; right_num = denominator * right.numerator; answer.denominator = right.denominator * denominator; answer.numerator = left_num - right_num; return answer; }
An Easier Way to Subtract Fractions Fraction Fraction::operator -(Fraction right) { Fraction left = *this; return (left + -right); }
int Fraction::operator <(Fraction right) { int left_int = numerator * right.denominator; int right_int = right.numerator * denominator; return (left_int < right_int); } int Fraction::operator >(Fraction right) { int left_int = numerator * right.denominator; int right_int = right.numerator * denominator; return (left_int > right_int); }
int Fraction::operator ==(Fraction right) { Fraction left = (*this); return !(left right); }