C Crash Course Joshua Barczak CMSC 435 UMBC
C++ Crash Course Joshua Barczak CMSC 435 UMBC
Not covered here • You should have learned C in 313 – Pointers – Compiling/Linking – Preprocessor (#include/#define) – Arrays – Structures/Unions • Basically the same in C++
Pointer Litmus Test struct Foo { int* q; int n; }; // Pointers in C++ are the same as C // If you can understand this code, you will be fine void Pointer. Test( Foo** p ) { p = *(&p); // useless. . . while( (*p)->q ) { int* q = p[0]->q; int* q 2 = q + (**p). n; while( q != q 2 ) (*(q++))++; p++; } }
Key Differences From Java • Manual memory management – No reference counting – No garbage collection – Destructors • • Explicit polymorphism Pointers/References Object Lifetime/Copying Templates
C++ Class class Thing : public Thing. Base { private: // these are private Widget* m_p. Widget; public: // these are public // This is a "default constructor" Thing() : Thing. Base() { m_p. Widget = 0; } // In C++, unlike C, NULL is always 0 // this is a "Copy Constructor" Thing( const Thing& t ) : Thing. Base(t) { m_p. Widget = new Widget(*t. m_p. Widget); } // This is another constructor Thing( int arg ) : Thing. Base() { m_p. Widget = new Widget(arg); } ~Thing() { delete m_p. Widget; } // This is a destructor int Foo() { return 5; } // This is a method definition void Bar( int argument ); // This is a method declaration protected: // these are private int m_Protected; };
Typical File Structure // File: Thing. h #ifndef _THING_H_ #define _THING_H_ // File: Thing. cpp // include necessary headers here #include "Thing. h" // include necessary headers here Thing: : Thing() { // initialize it } class Thing { public: Thing(); void Member( int argument ); }; void Thing: : Member( int arg ) { // code } void Non. Member ( float n ); #endif void Non. Member ( float n ) { // code }
Class vs Struct struct Foo { int n; int* p; // public }; class Foo { int n; int* p; // private };
Namespaces namespace foo { void Something(); } void Something. Else() { //Something(); // won't compile foo: : Something(); // will compile } using foo: : Something(); // either of these will work using namespace foo; void Something. Else() { Something(); // will compile }
Local Objects // C++ void Foo() { // t 1 and t 2 are different instances // t 2 is a copy of t 1 Thing t 1; Thing t 2 = t 1; } // Java void Foo() { // t 1 and t 2 are both null references Thing t 1; Thing t 2 = t 1; }
C++ References // References are gussied-up pointers void Foo() { Thing t; Thing& rt = t; rt. Func(); // Invokes ‘Func’ on t } void Null. Reference() { Thing* p = 0; // 0 is NULL in C++ Thing& q = *p; //q. Func(); // CRASH! } • C++ Reference – MUST be initialized – Can’t be changed – Easier to read than pointers • And harder to screw up – “Can’t” be NULL • Unless somebody screws up…
Parameter Passing void Pass. By. Pointer( Thing* p ) { p->Foo(); // calls 'Foo' on t via p *p. Foo(); // does the same thing } void Pass. By. Value( Thing x ) // x is copy-constructed at call-time { x. Foo(); // calls 'Foo' on x // calls 'x' destructor on exit } void Pass. By. Reference( Thing& rt ) { rt. Foo(); // calls 'Foo' on 't' via 'rt' } void Calls() { Thing t; Pass. By. Pointer(&t); Pass. By. Value(t); // makes a copy Pass. By. Reference(t); // does NOT make a copy! }
Object Lifetimes Thing g_Global. Thing; // Default-constructed at load-time, Destructed at exit void Create. Things() { Thing s 1; // stack-dynamic, default-constructed Thing s 2 = s; // stack-dynamic, copy-constructed Thing* p. Heap = new Thing(); // heap-dynamic, default-constructed Thing* p. Copy = new Thing( s 1 ); // heap-dynamic, copy-constructed Thing* p. Heap 50 = new Thing[50]; // heap-dynamic array of 50 default-constructed things Thing* p. Stack = &t; // pointer to stack memory Thing* p. Null = 0; // Do something useful with all those pointers… delete p. Heap; delete[] p. Heap 50; delete p. Null; //delete p. Stack; // calls destructor and frees memory // calls 50 destructors and frees memory // This is legal. It’s a no-op // CRASH! (Hopefully…) // destructors for s 1, s 2 called automatically }
Polymorphism class Base { public: int Foo() { return 1; } virtual int Bar() { return 1; } }; class Derived : public Base { public: int Foo() { return 2; } virtual int Bar() { return 2; } }; void Test( ) { Derived* p. Derived = new Derived(); Base* p. Base = static_cast<Base*>(p. Derived); p. Base->Foo(); // 1 p. Base->Bar(); // 2 p. Derived->Foo(); // 2 (1 in java!) p. Derived->Bar(); // 2 }
Virtual Destructors class Base { public: ~Base(); }; class Derived : public Base { public: ~Derived() { delete m_p. Thing; } Thing* m_p. Thing; }; void Delete. Me( Derived* p. Derived ) { Base* p. Base = static_cast<Base*>(p. Derived); delete p. Base; // invokes ~Base(). LEAKS MEMORY! }
Virtual Destructors class Base { public: virtual ~Base(); }; class Derived : public Base { public: ~Derived() { delete m_p. Thing; } Thing* m_p. Thing; }; void Delete. Me( Derived* p. Derived ) { Base* p. Base = static_cast<Base*>(p. Derived); delete p. Base; // invokes ~Derived() }
Abstract Classes class Foo { public: virtual void Method() = 0; // "Pure virtual" methods. virtual void Method 2() = 0; // Must be overriden by subclass void Get() { return m_a; }; // C++ isn't fussy about "abstract-ness" protected: int m_a; };
Casting • static_cast<> – Upcast/Downcast between related types – No runtime check • dynamic_cast<> – Runtime checks • reinterpret_cast<> – Use with care • C-style casts: (T*)p – Does static_cast if it knows the type
Multiple Inheritance class Base { }; class Sub 1 : public Base { }; class Sub 2 : public Base { }; class Diamond : public Sub 1, public Sub 2 { // 'Diamond' contains TWO copies of 'Base' // If Sub 1 or Sub 2 are polymorphic, 'Diamond' has two VTables // This situation is fraught with peril. . . };
Casting and MI class A { }; class B : public A {}; class C : public A {}; class D : public B, public C {}; p, p. C 2, p. B, p. A 2 p. C, p. A void Casts( D* p ) { C* p. C = static_cast<C*>(p); // safe upcast. Moves pointer A* p. A = static_cast<A*>(p. C); // safe upcast. B* p. B = p; // implied static cast, moves pointer A* p. A 2 = p. B; // implied static cast C* p. C 2 = reinterpret_cast<C*>(p); // UNSAFE! But compiles } A B A C D
C++ “Interfaces” class Interface 1 { public: virtual void Foo() = 0; virtual void Bar() = 0; }; class Interface 2 { public: virtual void Baz() = 0; }; class Foo : public Interface 1, public Interface 2 { public: virtual void Foo() { }; virtual void Bar() { }; virtual void Baz() { }; };
Const void Const( int* a, const int* b ) { *a = *b; // works *b = *a; // doesn't compile *const_cast<int*>(b) = *a; // works, but is EVIL! } class Thing { public: int Get() const { return m_it; } void Set(int i) { m_it = i; } void Broken. Set( int i ) const { m_it = i; } // doesn't compile }; void Const 2( Thing& thing 1, const Thing& thing 2 ) { thing 1. Set( thing 2. Get() ); // works thing 2. Set( thing 1. Get() ); // doesn't compile const_cast<Thing&>(thing 2). Set( thing 1. Get() ); // EVIL! }
Operator Overloading class Vec 2 { public: float x; float y; Vec 2() {} Vec 2( float xx, float yy ) : x(xx), y(yy) {} Vec 2& operator+=( const Vec 2& rhs ) { x += rhs. x; y += rhs. y; return *this; } Vec 2& operator*=( float a ) { x *= a; y *= a; return *this; } }; Vec 2 operator+( const Vec 2& lhs, const Vec 2& rhs ) { return Vec 2( lhs. x + rhs. x, lhs. y + rhs. y ); } Vec 2 operator*( const Vec 2& lhs, float rhs ) { return Vec 2( lhs. x*rhs, lhs. y*rhs ); } // WITHOUT OPERATOR OVERLOADING Vec 2 Verbose. Centroid( const Vec 2* p, int n ) { float x=0; float y=0; for( int i=0; i<n; i++ ) { x += p[i]. x; y += p[i]. y; } x *= (1. 0 f/n); y *= (1. 0 f/n); return Vec 2(x, y); } // WITH OPERATOR OVERLOADING Vec 2 Centroid( const Vec 2* p, int n ) { Vec 2 v(0, 0); for( int i=0; i<n; i++ ) v += p[i]; v *= (1. 0 f/n); return v; }
Operator Overloading class String { public: operator char*() { return m_p[i]; }; char& operator[](int i) { return m_p[i]; } const char& operator[](int i) const { return m_p[i]; } String operator+( const String& rhs ) const { String s = *this; // concatenate rhs onto s return s; } int Length() const; }; void Make. Xs( const String& s ) { for( int i=0; i<s. Length(); s++ ) s[i] = 'x'; } void Cast( String& s ) { // overloaded implicit conversion char* p = s; }
C++ Stream I/O // C #include <stdio. h> // C++ #include <iostream> //int printf( const char* fmt, … ); int main( int argc, char* argv[] ) { int n; std: : cout << "Hello worldn"; std: : cout << "You gave me: " << argc << "commandline args. n"; std: : cout << "Input please: "; std: : cin >> n; std: : cout << "Argument: " << n << " was " << argv[n] << std: : endl; return 0; } int main( int argc, char* argv[] ) { int n; printf("Hello worldn"); printf("You gave me: %d commandline args. n", argc ); printf("Input please: "); scanf( "%d", &n ); printf("Argument: %d was %s", n, argv[n] ); return 0; }
Overloading I/O std: : ostream& operator<<( std: : ostream& lhs, const String& rhs ) { lhs << “[“ << rhs. Get. Char. Pointer() << “]”; return lhs; } int main() { String a(“Are"); String b(“We"); String c(“There"); String d(“Yet”); std: : cout << a << b << c << d << “? n”; return 0; } Output: [Are][We][There][Yet]?
Templates template< class T > T Add. All( T* p, int n ) { T sum = 0; for( int i=0; i<n; i++ ) sum += p[n]; return sum; } void Foo( int* array 1, float* array 2, int n ) { int i = Add. All<int>( array 1, n ); // <int> optional here float f = Add. All<float>( array 2, n ); // <float> optional here }
Class Templates template< class T > class Scoped. Array { public: Scoped. Array( int n ) { m_p = new T[n]; }; ~Scoped. Array() { delete[] m_p; } const T& operator[]( int i ) const {return m_p[i]; } T& operator[]( int i ) { return m_p[i]; }; private: T* m_p; }; void Foo( int n ) { Scoped. Array<int> array(n); // use it. . . Memory is auto-freed. . . }
Class Templates #include <assert. h> template< class T, int N > // C++ allows integer template parameters class Checked. Array { public: T& operator[]( unsigned int i ) { assert( i < N ); return m_a[i]; } private: T m_a[N]; }; void Foo() { Checked. Array<float, 50> Fifty. Floats; }
Template Methods class Thing { public: template< class T > T* As( ) { return static_cast<T*>(this); }; }; void Example( Thing* p ) { Other* p. Other = p->As<Other>(); // …bit less typing }
Standard Template Library #include <vector> #include <list> #include <map> #include <string> #include <algorithm> std: : string std: : list<T> std: : vector<T> std: : map<TKey, TValue> // Java-like string class // Doubly-linked list // Dynamic array // Ordered associative array (red-black tree) std: : sort( p, p+n ); std: : sort( p, p+n, f ); std: : find( p, p+n, e ); std: : fill( p, p+n, e ); std: : partition( p, p+n, f ); std: : swap( T&a, T& b ); std: : count( p, p+n, e ); // sort range using < // sort range using compare function // linear search of range // fill range // sort range by predicate // swap values // count number of e’s in range
Extra Slides
Casting Examples… class A{ public: virtual ~A(); }; class B : public A {}; class C : public A {}; class D : public B, public C {}; void Casts( D* p, A& q 1, A& q 2 ) { C* p. C = static_cast<C*>(p); // safe upcast. WILL alter pointer value A* p. A = static_cast<A*>(p. C); // safe upcast. May alter pointer value A* p. A 2 = p. C; // implicit static_cast C* p. C 2 = reinterpret_cast<C*>(p); // UNSAFE! WILL NOT alter pointer value //A* p. A 1 = static_cast<A*>(p); // won't compile (AMBIGUOUS) A* p. A 3 = reinterpret_cast<A*>(p); // WILL compile, but is UNSAFE! //C* p. C 3 = p. A 2; // won't compile (downcasts require static_cast) B& rb 1 = static_cast<B&>(q 1); // safe downcasts C& rc 1 = static_cast<C&>(q 2); B& rb 2 = static_cast<B&>(q 2); // UNSAFE downcasts C& rc 2 = static_cast<C&>(q 1); // NO runtime checks! // These WILL get runtime checks, B& rb 3 = dynamic_cast<B&>(q 1); // safe downcasts C& rc 3 = dynamic_cast<C&>(q 2); B& rb 4 = dynamic_cast<B&>(q 2); // UNSAFE downcasts C& rc 4 = dynamic_cast<C&>(q 1); } int main() { B b; C c; D d; Casts( &d, b, c ); }
More Destructor Examples class Foo { public: Foo( Thing* pt ) : m_p. Thing(pt) {} ~Foo() { delete m_p. Thing; } private: // each 'Foo' instance "owns" its 'Thing' Thing* m_p. Thing; }; void The. Bad. And. The. Ugly() { Thing stack; Foo foo(&stack); // will crash on return Foo* p. Foo = new Foo( &stack ); delete p. Foo; // crash // not deleted. Leaks memory Foo* p. Leaky = new Foo( new Thing() ); void The. Good( ) { Foo stack( new Thing() ); Foo* p. Heap = new Foo( new Thing() ); Thing* p. Thing = new Thing(); Foo* p. F 1 = new Foo( p. Thing ); Foo* p. F 2 = new Foo( p. Thing ); delete p. F 1; // works delete p. F 2; // double-delete. Crash in ~Foo() delete p. Heap; // Calls p. Heap->~Foo() // stack. ~Foo() called at exit } }
C-Style cast class A; class F; // forward declarations (incomplete types) F* Foo( A* p ) { return (F*)p; // reinterpret_cast. Potentially dangerous } class A{}; class F : public A{}; // complete types F* Bar( A* p ){ return (F*)p; // static_cast. Safe upcast. }
- Slides: 34