Brown Bag #3 Return of the C++. Topics Common C++ “Gotchas” Polymorphism Best Practices ...
-
Upload
nevaeh-gascoyne -
Category
Documents
-
view
213 -
download
1
Transcript of Brown Bag #3 Return of the C++. Topics Common C++ “Gotchas” Polymorphism Best Practices ...
Brown Bag #3Return of the C++
Topics
Common C++ “Gotchas”
Polymorphism
Best Practices
Useful Titbits
Common C++ “Gotchas”
Circular dependencies
Slicing
Rule of Three
Circular Dependencies
Problem:
Two classes that depend on each other
Can’t use #include in both headers
// file: A.h
#include “B.h”class A { B _b; };
// file: B.h
#include "A.h”class B { A _a; };
// file: A.h
class B; class A { B& _b; };
// file: B.h
class A;class B { A& _a; };
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 operatorMyClass& MyClass::operator= (const MyClass& other){ if (this != &other) { // Do stuff }
return *this;}
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
Slicing
Problem:
Loss of derived class functionality
Occurs when derived class copied into base class
Especially when passing by value
void Foo( BaseClass baseParam );
...
DerivedClass myDerived;Foo( myDerived );
Solution:
Pass by reference or pointer!
Avoid concrete base classes
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.
class Animal{ virtual int GetNumLegs();};
class Dog : public Animal{ virtual int GetNumLegs();};
class Octopus : public Animal{ virtual int GetNumLegs();};
int Animal::GetNumLegs(){ return 0;}
int Dog::GetNumLegs(){ return 4;}
int Octopus::GetNumLegs(){ return 8;}
Implicit Override
C++ traditionally does not require use of the ‘override’ keyword.
class Parent{ virtual void Foo(int i);};
class Child : public Parent{ virtual void Foo(float i);};
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.
class Parent{ virtual void Foo(int i);};
class Child : public Parent{ virtual void Foo(float i) 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.
class Parent{ virtual void Foo() final;};
class Child : public Parent{ virtual void Foo() 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.class Parent{ virtual void Foo() = 0;};
class Child : public Parent{ virtual void Foo();};
Parent parentObject; // ERRORChild childObject; // 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.
__interface IDancer{ void Dance();};
class Fireman : public IDancer{ virtual void Dance();};
class Butcher : public IDancer{ virtual void Dance();};
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;Fireman fireman;Butcher butcher;
dancers.push_back(fireman);dancers.push_back(butcher);
std::for_each(dancers.begin(), dancers.end(), [](IDancer dancer){ dancer.Dance();});
Multiple Inheritance
Consider the following example:
Animal
Mammal WingedAnimal
Bat
class Animal{ virtual void Eat();};
class Mammal : public Animal{ …};
class WingedAnimal : public Animal{ …};
class Bat : public Mammal, public WingedAnimal{ …};
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:
class Animal{ virtual void Eat();};
class Mammal : public virtual Animal{ …};
class WingedAnimal : public virtual Animal{ …};
class Bat : public Mammal, public WingedAnimal{ …};
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
1. const Thing* a = new Thing();2. Thing const * b = new Thing();3. Thing* const c = new Thing();
4. const Thing* const d = new Thing();
1. Pointer to constant object - Pointer cannot change object
2. Same as 1.
3. Constant pointer to object - Pointer itself cannot change
4. All the const -Neither pointe or pointed can change
Const FTW
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
void Foo( const Thing& input );void Foo( Thing const & input );
Thing GetData() const;
const member functions (getters)
void Foo( Thing input );void Foo( Thing input );
Preprocessor FTL
Avoid #define literals
Not type-safe
Not scoped
Use initialized constant instead
#define SuperImportant = 42;#define SuperImportant = 42;const int SuperImportant = 42;
Avoid #define pseudo-functions
Look like function calls, aren’t
Same type/scope problems
Use initialized constant instead
#define MAX(a, b) ((a < b) ? b : a);#define MAX(a, b) ((a < b) ? b : a);
inline int MAX(int a, int b){ return (a < b) ? b : a;}
Enums FTW
struct MyEnum{ enum Enum { MAX };};
enum class MyEnum { MAX};
Nicer and safer than preprocessor definitions
Enum classes/structs (C++ 11)
Old: Wrap Enums in struct
Now type-safe in C++ 11
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.
char Foo();int i = 0;
decltype(i) a; // a is an intdecltype(0) b; // b is an intdecltype(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).
int myArray[6] = {1, 2, 3, 4, 5, 6};int arrayTotal = 0;
for (int &i : myArray){ arrayTotal += i;}
std::cout << “The sum of all values is “ << arrayTotal << “.\n”; // 21
Singleton Pattern
Limit to one class instance
No public constructors
Single static instance
Semi-controversial design pattern
Don’t overuse it
class S{public: static S& getInstance() { static S instance; return instance; }
private: S(S const&);
// Don't implement void operator=(S const&);};
Stolen from: http://stackoverflow.com/questions/1008019/c-singleton-design-pattern
Treat Warnings as Errors
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