Lesson 3 recap
C++ extra syntax
- Default parameter values
- Function overloading
Object Oriented
- Terminology
- Philosophy
- Programming
- Objects life cycle
- Inheritance
- Access control (public/private/protected)
- Initialisation list
In C++ it is possible to provide default values to to parameters
void foo(int x = 100);
int main() {
foo(); // x will default to 100
foo(17);// x will be 17
}
It is not possible to add a default to a parameter if there are more parameters without default to it's right.
void foo(int x = 100, int y); // Invalid
void foo(int x; int y = 100, int z); // Invalid
void foo(int x; int y = 1, int z = 2); // Valid
It is advisable to only add default's to the declaration(header files) the caller during compilation is supposed to only see the declarations.
In C++ the function signature is combined with the parameters singature
allowing to have multiple functions with the same name
int add(int a, int b)
{
return a + b;
}
double add(double a, double b)
{
return a + b;
}
Notice: The functions can not differ only on the return value.
// Object Oriented terminology
#include <iostream>
#include <string>
// A class is a new user defined type
// It is composed of attributes(the data) and methods(the actions that can be performed on the data)
//
// Constructor & Destructor shouldn't be considered as functions as such but rather as
// Initialization and cleanup.
class Example {
public: // Access control (Can be private/public/protected)
Example() {} // Constructor (aka ctor): has the class name and return no values
~Example() {} // Destructor (aka dtor): starts with a tilde (~) has the class name and returns no value
int attr1; // Attribute or field or member data
char* attr2;
void action1() {} // member function or method
};
int main()
{
Example e; // e is an object (or instance) of type (or class) Example
}
1. Abstraction - ability to introduce new types
2. Encapsulation
i. Object Oriented Design forces to encapsulate (pack together) related attributes and actions
ii. additional compiler directives help the compiler maintain the encapsulation (public / private / protected)
3. Inheritance - The ability to inherit and specialise behaviour
4. Polymorphism - To be discussed
class Mouse { /* ... */ }; // Introduce a new type tiger
class Mouse {
private: // Enforce encapsulation by preventing the outside seeing internal implementation
int numberOfTeeth;
public:
void bite(); // A Tiger encapsulates all its actions and attributes
};
class MightyMouse : public Mouse {
/* ... */
public:
void fly();
};
To allow the programmer to enforce encapsulation, class/struct Access modifiers were introduced.
Public: Allows access to fields and actions to everybody
class AllPublic {
public:
int field1;
void foo() { /* foo's code */ }
};
/* ... */
AllPublic ap:
ap.field1 = 1; // Access field1
ap.foo(); // Execute ap's foo method
Private: Allows access only from the class code itself
class AllPrivate {
private:
int field1;
void foo() { field1 = 10; } // Legal
};
/* ... */
AllPrivate ap:
ap.field1 = 1; // Disallowed by compiler
ap.foo(); // Disallowed by compiler
The keyword class introduced to allow creation of structs with functions (methods). Because the functions are part of the structs, the data (attributes) doesn't need to be visible from the outside.
The struct also support the new OO constructs, the difference between for backward computability is that everything is public by default for struct. While for class all is private by default.
= and =
- A struct & class compiled by a C++ compiler will have Constructor & Destructor generated for them.
- A class/struct in C++ is not obliged to have methods nor attributes. In C an empty struct is illegal.
struct A {
/* code */
}
class A {
public:
/* code */
}
struct A {
private:
/* code */
}
class A {
/* code */
}
C++ object model ensures that a well implemented class will have it's object always in a valid state. To enforce that each object created either on the stack or Heap, passes through a construction.
A default constructor is generated by the compiler if there was no constructor supplied by the programmer.
A Constructor is a function that returns no value and has the same name as it's class.
class Mouse {
public:
Mouse() { /* Construction code */ } // default constructor
~Mouse() { /* Destruction code */ }
};
A destructor is called when the object is going out of scope or when a delete is called on an allocated object
{
Mouse m;
} // M is going out of scope, destructor called
Mouse* pMouse = new Mouse(); // Memory is allocated and than constructor is called
delete pMouse; // Destructor called and than memory is freed.
Inheritance is allowed by publicly inheriting from a another class
public Mouse {};
public MightyMouse : public Mouse {}
Inheritance allows for code reuse to be implicit from one class to the another.
This can also be viewed as specialisation, if the abstraction was done correctly, than the base class are more generic and the derived class more specific
Output
=>
class Value
{
public:
Value(const string& in_name): name(in_name)
{ cout << "ctor Value: (" << name << ")" << endl; }
~Value() { cout << "dtor Value: (" << name << ")" << endl; }
private:
string name;
};
class Base
{
public:
Base():baseVal("Base") { cout << "ctor Base:" << endl; }
~Base() { cout << "dtor Base:" << endl; }
private:
Value baseVal;
};
class Derived : public Base
{
public:
Derived(): derivedVal("Derived") { cout << "ctor derived: " << endl; }
~Derived() { cout << "dtor Derived:" << endl; }
private:
Value derivedVal;
};
int main() {
{
Derived d;
}
}
ctor Value: (Base)
ctor Base:
ctor Value: (Derived)
ctor derived:
dtor Derived:
dtor Value: (Derived)
dtor Base:
dtor Value: (Base)
OO - Constructor Initialisation list
Because as we saw in previous slide the initialisation sequence in C++, requires Base's than attributes and than self.
By the time the constructor code is reached it is expected that both Bases and attributes are already constructed.
// Excerpt from previous slide
class Base
{
public:
// Initialisation for baseVal is called before Base constructor
Base(const string& in_name = "Base"):baseVal(in_name)
{ /* code */ }
~Base() { cout << "dtor Base:" << endl; }
private:
Value baseVal;
};
class Derived : public Base
{
public:
// Initialisation of both Base and derived takes place before Derived constructor
Derived():Base("Base of Derived"), derivedVal("Derived") { cout << "ctor derived: " << endl; }
~Derived() { cout << "dtor Derived:" << endl; }
private:
Value derivedVal;
};