440 likes | 568 Views
Brown Bag #2. Advanced C++. Topics. Templates Standard Template Library (STL) Pointers and Smart Pointers Exceptions Lambda Expressions Tips and Tricks!. Templates. Generic code that works with many data types Encourages code reuse Turing-complete
E N D
Brown Bag #2 Advanced C++
Topics • Templates • Standard Template Library (STL) • Pointers and Smart Pointers • Exceptions • Lambda Expressions • Tips and Tricks!
Templates • Generic code that works with many data types • Encourages code reuse • Turing-complete • Template metaprogramming (not covered) • Beware scary compiler/linker errors
Function Templates int Square(intnum) { returnnum * num; } • A family of functions • Uses a generic type • On demand compilation • Compiler can deduce types • Type-safe template<typenameT> T Square(Tnum) { returnnum * num; } float result = Square(5.0f); int result = Square(2);
Class Templates template<classT> classThing { public: Thing(T data) : m_data(data) {} TGetData() const { returnm_data; } private: T m_data; }; • class and typename are interchangeable • Explicit type specification • Declaration + implementation in same file • Can template methods not just whole classes • Great for containers Thing<int> myThing = Thing<int>(50); int data = myThing.GetData();
Standard Template Library (STL) • Containers • vector • list • map • string • Algorithms • for_each • find • sort • Iterators • auto keyword (not directly relevant, but handy)
vector • Dynamic array • Access item at index = constant time • Iterate over all elements = linear time std::vector<Thing> myVector; myVector[0].foo(); std::vector<Thing>::iterator for ( autoiter = myVector.begin(); iter!= myVector.end(); ++iter) { iter->foo(); }
for_each #include <algorithm> #include <vector> usingnamespacestd; • voidmyFunction (inti) { cout << " " << i; } • intmain() • { • vector<int> myVector; • myVector.push_back(10); • myVector.push_back(20); • for_each (myVector.begin(), myVector.end(), myFunction); • }
string • Special container type – sequence of characters. • Contains useful functions and common STL container functionality. #include <string> • int main() { std::string test(“Hello World!”); std::cout << “Length of string is “ << test.size() << “.\n”; // 12 }
map • Associative container that stores values as a <key, value> pair. • An array uses an integer as the key type. • Each element must have a unique key. • Map containers support iterators that return key and value. #include <map> • using namespace std; • int main() { map<string, int> testMap; testMap.insert(map<string, int>::value_type(“Hello”, 5); intmyInt = testMap[“Hello”]; cout << “Value contained in element with key ‘Hello’ is “ << myInt << “.\n”; // 5 }
Pointers • Pointers are references to memory blocks which contain data (or an instruction). • We access this data using the reference (&) and dereference (*) operators.
Dereference Operator (*) • If a pointer is a memory address, how do we access the object at that location? • Dereference a pointer to obtain the value at the memory address.
Reference Operator (&) • How do we alter a value at a given memory address and not just a copy? • Use the Reference operator to obtain a variable's memory address.
Pointer to a Pointer • It is possible to have a pointer that points to another pointer.
Pointer to a Pointer • Ever seen a DirectX function where you pass in a reference to a pointer? Check the argument list - that's what is happening there!
Class and Struct Pointers • You can create pointers to struct and class objects using the 'new' keyword: • Lets try setting the value of ack::bar to 5:
Class and Struct Pointers • This is C# syntax - doesn't work in C++!
The -> Operator • Like before, we must dereference the pointer before we can access the object! • There's a nicer way: • The -> operator dereferences a class or struct pointer and gives access to its members. • This is known as "syntactic sugar".
‘delete’ and Null Pointers • When you de-allocate a pointer using the 'delete' keyword, it is common to set the pointer's value to 0: • A pointer whose value is 0 is known as a null pointer.
Smart Pointers • These can be found in the Standard Library as part of the <memory> header file. • There are three types: • unique_ptr • shared_ptr • weak_ptr
Smart Pointer Syntax • Pointers declared using template-style syntax: • * and & operators can be utilised as normal: • Memory is de-allocated at the end: • However, de-allocation does not need to be done manually!
Reference Counting • Smart pointers count the number of references to an object in memory. • When a pointer leaves scope, the reference count is decremented. • When the reference count reaches 0, the memory is de-allocated.
unique_ptr • Allows for only one reference to a stored object - it is unique. • This is an invalid operation. However, ownership can be transferred: • When memory is de-allocated:
shared_ptr • shared_ptrs allow for multiple pointers to reference the same memory address.
weak_ptr • To avoid circular references, we use weak_ptrs: • In order to access the shared_ptr, we use weak_ptr::lock(): • When the shared_ptr is deallocated, weak_ptr::lock() will return an empty shared_ptr object:
nullptr • Traditionally, a null pointer is defined as NULL, or 0. Consider the following: • How do we distinguish between 0 and a null pointer? • C++11 introduces the nullptr type: • Now we no longer need to worry about confusing null pointers and int values!
Smart Pointers: Summary • Smart pointers allow for all the same functionality of a standard pointer. • Makes use of Reference Counting for automatic de-allocation. • unique_ptr makes it easier to store single references to objects at a time. • shared_ptr allows for multiple pointers to share memory. • weak_ptr allows for accessing shared_ptr objects with easy clean-up. • nullptr helps to clearly distinguish from numeric values.
Exceptions try { throw 20; } catch (int e) { cout << “Exception:" << e << endl; } • Handle exceptional runtime errors • Unwinds stack (releases local variables, etc.) • Used by standard library / STL • Standard exceptions (bad_alloc, bad_cast, etc.) • Custom exceptions (extend std::exception) • Exception specifications floatfoo(charparam) throw (int);
Exceptions: The Good • Cleaner than error codes • Much nicer for deeply-nested functions • Separates error-handling from program flow • User-definable to carry detailed information • Catch constructor errors
Resource Acquisition Is Initialization (RAII) • Only destructors are guaranteed to run after an exception is hit • Use destructors to prevent resources leaks • Doesn’t require messy try/catch blocks • voidfoo(void){std::unique_ptr<Thing>myThing(new Thing() );myThing->Something();}
Exceptions: The Bad, The Ugly • Multiple program exit points • Changes program flow, maybe harder to debug • Make debugger break on exceptions in Debug • Potential to leak resources if misused • Use smart pointers, etc. to avoid • Exception-safe code can be hard to write • Don’t throw in destructors • Only throw on exceptionalerrors • Hard to introduce to existing code
Lambda Expressions [ ] () mutable throw() –> int { } • Related to the concept of anonymous functions. • Helps to solve the problems of function objects and function pointers. • Function pointer has minimal syntactic overhead but does not retain state. • Function object retains state but requires the overhead of a class definition. • Lambdas feature minimal overhead and can retain state within the scope in which they are defined.
Lambda Expressions: Example • voidLambdaExample() { automyLambda = [](int x, int y) -> int { return (x * 2) + y; }; int a = 3; intb = 4; intc = myLambda(a, b); std::cout << “The value of c is “ << c << “.\n”; // 10 int d = myLambda(c, b); std::cout << “The value of d is “ << d << “.\n”; // 24 }
Lambda Expressions: Syntax • Capture Clause: [ ] • Used to access variables from the scope enclosing the lambda. • Can be passed by reference or value (e.g. &x, y). • Default capture mode can be specified using & or = at the beginning for reference or value captures respectively (e.g. [&, x] or [=, y]). • ‘this’ pointer provides access to member variables of the enclosing class (e.g. [this]). • Parameter List: () • Specifies the parameters passed into the function, as with a regular function declaration. • Mutable Specification: mutable • Allows values captured by reference to be modified within the function. • Will not change the original value, only the local copy.
Lambda Expressions: Syntax (cont.) • Throw Specification: throw() • Specifies if the lambda can throw an exception. • throw() specifies no exception can be thrown. • throw(T) specifies an exception of type T can be thrown. • Return Type: -> T • Follows trailing return-type syntax introduced in C++11. • Explicitly specifies the return value of the function. • Can be implicitly implied via a return statement in the function body. • Function Body: { } • Defines the instructions to be performed, as with a standard function.
Lambda Expressions: Example Revisited • voidLambdaExample() { automyLambda = [](int x, int y) -> int { return (x * 2) + y; }; int a = 3; intb = 4; intc = myLambda(a, b); std::cout << “The value of c is “ << c << “.\n”; // 10 int d = myLambda(c, b); std::cout << “The value of d is “ << d << “.\n”; // 24 }
Why use Lambda Expressions? • #include <vector> • #include <algorithm> • intmain() • { • std::vector<int> myVector; • myVector.push_back(10); • myVector.push_back(20); • inttotalCount = 0; • for_each (myVector.begin(), myVector.end(), [&totalCount](int x) • { • totalCount += x; • }); • std::cout << “The total of all values in myVector is “ << totalCount << • “.\n”; // 30 • } • Iterator functions:
Why use Lambda Expressions? • #include <ppltasks.h> • usingnamespace Concurrency; • intmain() • { • autodoubleNum = [](int x) { return x * 2; }; • autoincrementNum = [](int x) { return ++x; }; • autostartTask = create_task([]() -> int { return 5; }); • intfinalNum = • startTask.then(doubleNum).then(incrementNum).then(doubleNum).get(); • std::cout << “The value of finalNum is “ << finalNum << “.\n”; // 22 • } • Asynchronous tasks:
Const FTW • Prefer pass-by-reference-to-const to pass-by-value (item #20) • Avoid unnecessary constructors/destructor calls • Still guarantee to caller that object won’t be changed voidFoo( constThing& input ); • const member functions (getters) ThingGetData() const;
Enums FTW • Nicer and safer than pre-processor definitions • Enum classes/structs (C++ 11) • Old: Wrap Enums in struct • Now type-safe in C++ 11 structMyEnum { enumEnum { MAX }; }; enumclassMyEnum { MAX };
Further Reading • Microsoft Developers Network (MSDN) • CPlusPlus.com