300 likes | 457 Views
Brown Bag #3. Return of the C ++. Topics. Common C++ “Gotchas” Polymorphism Best Practices Useful Titbits. Common C++ “Gotchas”. Circular dependencies Slicing Rule of Three. // file: A.h class B; class A { B& _b; }; // file: B.h class A; class B { A& _a; }; .
E N D
Brown Bag #3 Return of the C++
Topics • Common C++ “Gotchas” • Polymorphism • Best Practices • Useful Titbits
Common C++ “Gotchas” • Circular dependencies • Slicing • Rule of Three
// file: A.h • classB; • classA • { • B& _b; • }; • // file: B.h • classA; • class B • { • A& _a; • }; // file: A.h • #include “B.h” class A { B _b; }; // file: B.h • #include "A.h” class B { A _a; }; Circular Dependencies • Problem: • Two classes that depend on each other • Can’t use #include in both headers • Solution: • Use forward declarations! • Use (smart) pointers / references for members • Limit includes in headers (helps compilation)
Rule of Three • If you define one of these, define all three: • Destructor • Copy constructor • Assignment operator • Otherwise implicitly generated • Latter 2 copy all class members • Copies pointers not objects • Want move/swap semantics? • Call base version from derived classes • Rule of Two: RAII destructors MyClass& MyClass::operator= (constMyClass& other) { if(this != &other) { // Do stuff } return*this; }
Slicing • Problem: • Loss of derived class functionality • Occurs when derived class copied into base class • Especially when passing by value • Solution: • Pass by reference or pointer! • Avoid concrete base classes voidFoo( BaseClassbaseParam); ... DerivedClassmyDerived; Foo( myDerived );
Polymorphism • ‘virtual’ • Implicit Override • Explicit Override • ‘final’ • Abstract Classes • Interfaces • Multiple Inheritance • Virtual Class Inheritance
‘virtual’ • Used to declare virtual functions. • Virtual functions can have functionality overwritten in child classes. classAnimal { virtualintGetNumLegs(); }; classDog:publicAnimal { virtualintGetNumLegs(); }; classOctopus:publicAnimal { virtualintGetNumLegs(); }; intAnimal::GetNumLegs() { return 0; } intDog::GetNumLegs() { return 4; } intOctopus::GetNumLegs() { return 8; }
Implicit Override • C++ traditionally does not require use of the ‘override’ keyword. classParent { virtualvoidFoo(inti); }; classChild :publicParent { virtualvoidFoo(floati); }; • Child::Foo does not override Parent::Foo as they have different signatures. • Compiler will not raise an error over this – this is valid declaration.
Explicit Override • C++11 introduces the ‘override’ keyword to ensure virtual functions are overwritten. classParent { virtualvoidFoo(inti); }; classChild :publicParent { virtualvoidFoo(floati)override; }; • If the base class does not contain a virtual function with the same signature, the compiler will throw an error. • Useful to ensure functions are overwritten correctly.
‘final’ • C++11 also introduces the ‘final’ keyword. • This ensures that a virtual function cannot be overwritten in child classes. classParent { virtualvoidFoo() final; }; classChild :publicParent { virtualvoidFoo()override; }; • Attempting to override a virtual function declared as final will cause the compiler to throw an error.
Abstract Classes • An abstract class is one which you cannot instantiate. • Contains virtual function(s) which must be overwritten in the child class. • A class is abstract if it contains at least pure virtual function. classParent { virtualvoidFoo() = 0; }; classChild :publicParent { virtualvoidFoo(); }; ParentparentObject; // ERROR ChildchildObject; // OK
Interfaces • Similar to an abstract class whose functions are all pure virtual. • Defines certain functionality that a class must implement. • Implementation of functions is individual to each class. __interfaceIDancer { voidDance(); }; classFireman:publicIDancer { virtualvoidDance(); }; classButcher:publicIDancer { virtualvoidDance(); };
Interfaces (cont.) • Objects that implement an interface can be cast to their interface type. • Allows for easy communication between otherwise unrelated object types. • Easier manipulation of objects. std::vector<IDancer> dancers; Firemanfireman; Butcherbutcher; dancers.push_back(fireman); dancers.push_back(butcher); std::for_each(dancers.begin(), dancers.end(), [](IDancerdancer) { dancer.Dance(); });
Multiple Inheritance Animal • Consider the following example: classAnimal { virtualvoidEat(); }; classMammal : publicAnimal { … }; classWingedAnimal: publicAnimal { … }; classBat : publicMammal, publicWingedAnimal { … }; Mammal WingedAnimal Bat
Multiple Inheritance (cont). • If we call Bat::Eat(), which function do we call? • It is an ambiguous function call. • This is because we have two Animal base classes. • Static cast to Animal is also ambiguous.
Virtual Class Inheritance • We can use the ‘virtual’ keyword when inheriting from a class: classAnimal { virtualvoidEat(); }; classMammal : publicvirtualAnimal { … }; classWingedAnimal: publicvirtualAnimal { … }; classBat : publicMammal, publicWingedAnimal { … };
Virtual Class Inheritance (cont.) • The ‘virtual’ keyword will ensure that when a Bat object is created, the Animal instance used by Mammal and WingedAnimal will be the same. • This will remove any ambiguity from calls to Bat::Eat(). • Will also allow direct casting of Bat to Animal.
Best Practices • Const WTF • Const FTW • Preprocessor FTL • Enums FTW
Const WTF constThing* a = new Thing(); Thing const* b = newThing(); Thing* constc = newThing(); constThing* constd = newThing(); • Pointer to constant object - Pointer cannot change object • Same as 1. • Constant pointer to object - Pointer itself cannot change • All the const - Neither pointe or pointed can change
Const FTW voidFoo( Thinginput); voidFoo( Thinginput); • Prefer pass-by-reference-to-const to pass-by-value (item #20) • Avoids unnecessary constructors/destructor calls • Still guarantee to caller that object won’t be changed voidFoo( constThing& input); voidFoo( Thing const& input); • const member functions (getters) ThingGetData() const;
Preprocessor FTL #defineSuperImportant = 42; #defineSuperImportant = 42; constintSuperImportant = 42; • Avoid #define literals • Not type-safe • Not scoped • Use initialized constant instead #defineMAX(a, b) ((a < b) ? b : a); #defineMAX(a, b) ((a < b) ? b : a); inlineint MAX(inta, intb) { return(a < b) ? b : a; } • Avoid #define pseudo-functions • Look like function calls, aren’t • Same type/scope problems • Use initialized constant instead
Enums FTW • Nicer and safer than preprocessor definitions • Enum classes/structs (C++ 11) • Old: Wrap Enums in struct • Now type-safe in C++ 11 structMyEnum { enumEnum { MAX }; }; enumclassMyEnum { MAX };
Useful Titbits • Type Inference • Range-Based For Loop • Singleton Design Pattern • Treat Warnings as Errors • Visual Assist X
Type Inference (decltype) • C++11 introduces ‘decltype’ which can be used to infer the type of an object based on the declared value of another. • Useful in conjunction with ‘auto’ when heavy operator overloading and casting is required. charFoo(); inti = 0; decltype(i) a; // a is an int decltype(0) b; // b is an int decltype(Foo()) c; // c is a char
Ranged-Based For Loop • An easier way to iterate through all the elements in a sequence of objects. • Supported by: • C-style arrays • Initializer lists • Types that implement begin() and end() iterators (STL Containers). intmyArray[6] = {1, 2, 3, 4, 5, 6}; intarrayTotal = 0; for (int &i : myArray) { arrayTotal += i; } std::cout<< “The sum of all values is “ << arrayTotal << “.\n”; // 21
Singleton Pattern classS { public: staticS& getInstance() { staticS instance; returninstance; } private: S(Sconst&); // Don't implement voidoperator=(Sconst&); }; • Limit to one class instance • No public constructors • Single static instance • Semi-controversial design pattern • Don’t overuse it Stolen from: http://stackoverflow.com/questions/1008019/c-singleton-design-pattern
Visual Assist X • Intellisense++ • Refactoring • Improved syntax highlighting • Keyboard shortcuts • Jump between header/source • £30 for students
Further Reading • Microsoft Developers Network (MSDN) • CPlusPlus.com