== Introduction == C++ vs C - Simplifies the job of reusing code - Provides a standard library - Syntax to encapsulate data - Parametric polymorphism - Exceptions - Compare std::string vs char* == Memory Allocation == - Static allocation // Data with addresses known at compilation time - global variables - static variables - program code - string literals - Stack allocation // data necessary to activate a function - local variables - function parameters - return values - the return address of functions - Heap allocation // everything that needs to outlive the function that creates it - malloc - new == Abstract Data Types == - Encapsulation reduces the cost of modifying software - Implementation changes often, interfaces don't - A TAD is a type described by the operations that it supports - In C++, a TAD is implemented via classes and structs // Example of header file: #ifndef HEADER_H #define HEADER_H struct Point { Point (double xx, double yy) : x(xx), y(yy) {} double getX(); double getY(); std::string toString(); private: double x; double y; }; #endif // Example of implementation file: #include "header.h" double Point::getX() { return x; } double Point::getY() { return y; } std::string Point::toString() { return "(" + std::to_string(x) + ", " + std::to_string(y) + ")"; } == Lists and Trees == - Lists and trees are recursive data-structures - Operations that manipulate them are naturally recursive - A list is either nothing, or an element plus a list - A tree is either nothing, or an element plus a left tree plus a right tree - 'const' is a kind of documentation implemented at the compiler level - Removing it does not hurt compilation - keeping it helps document the program - and helps the compiler to generate more efficient code == Good practices == - Choose good names - Comment the code - Use verifiable documentation (Names of types, const, restrict) - Avoid aliasing whenever possible - Avoid type casts - Avoid macros if possible (const, inline) - Avoid too many nesting levels - Indent the program - Limit the length of functions - Limit the number of responsibilities of modules - Use exceptions instead of error codes - Use coding guidelines - Reuse standard library whenever you can - Abuse assertions == Common Mistakes == - Remember: in C++, parameters and return values are passed by copy! - Remember: updates in a pointer might change other pointers! - Remember: you cannot access local variables after the function where they exist returns! - Remember: you cannot access the contents of a pointer after deletion. - Remember: arrays cannot be accessed outside their bounds! - Remember: if a function returns a value, it must do it through any path that the function contains. - Remember: the expression "if(a = x)" is valid in C/C++! - Remember: every variable must be initialized before being used! - Remember: there are different ways to combine ==, !=, && and ||, and several of them make no sense! - Remember: the ternary operator has very low priority! == Examples of TADs == - set - map - vector == Object Oriented Programming == - OOP is the humanization of data - Data knows how to do things - That makes encapsulation very natural - Important aspects of OOP: subtyping and dynamic dispatch - Subtyping: the subtype can be used wherever the type is expected - Dynamic dispatch: the target of a call depends on the objects dynamic type - In C++, subtyping is produced through inheritance - In C++, dynamic dispatch is achieved through virtual calls == Software Modeling == - UML is a collection of models to describe object oriented software - The class diagram lets us describe the relation between classes - The class representation has tree parts: name, attributes and operations - Inheritance is an arrow with an open triangle - Association is a simple line - Association by composition is a line with a filled diamond - Composition implies ownership: the owned does not exist without the owner - Association by aggregation is a line with an empty diamond - Aggregation does not imply ownership == Classes == - Class variables: only one per class - all the objects share the same class variable - these variables are marked 'static' - Instance variable: one per object - these are the fields of objects not marked 'static' - Method overloading: different methods can have the same name - methods are distinguished by signature - signature is the type of the arguments - We can use the keyword 'override' to improve documentation - Overridden method must be virtual - When objects are copied, the copy-constructor is invoked - If we make the copy-constructor private, the object cannot be assined - Consequently, it cannot be passed as an argument - We can use the 'this' pointer to refer to the current object == Data Encapsulation == - C++ has three access modifiers: public, protected and private - public: visible everywhere - protected: visible in the class, and in the subclasses - private: visible only in the class - Friend classes can have access to private properties of their friends - It is possible to know the type of objects dynamically: - dynamic_cast(o) - #include + typeid(T).name() + typeid(o).name() - Usually, runtime type queries are not a good practice - code is less open for extension == Inheritance vs Composition == - Inheritance lets C++ meet the open-closed principle - C++ supports multiple inheritance - Multiple inheritance might lead to the "Diamond Problem" - Ambiguity when calling properties from subclass - Multiple invocations of the constructor - Inheritance implies an "is-a" relationship - Composition implies a "has-a" relationship - Composition has some advantages over inheritance - Easier to extend - No risk to break Liskov Substitution Principle - Simplifies the semantics of the program