- A namespace is an identified declarative region that avoids naming conflicts.
- Identifiers within a namespace are accessed using the scope operator
::. - The using keyword avoids having to scope each object within the namespace
#include <iostream>
namespace myFirstNamespace {
int a = 10;
}
namespace mySecondNamespace {
int a = 12;
}
int main() {
int c = myFirstNamespace::a + mySecondNamespace::a;
std::cout << c << std::endl;
using myFirstNamespace::a;
c = a + mySecondNamespace::a;
std::cout << c << std::endl;
return 0;
}- Data is output using the insertion operator
<<. - Data is retrieved using the extraction operator
>> - cout is an object of the ostream class
#include <iostream>
int main() {
int c;
std::cin >> c;
std::cout << c << std::endl;
return 0;
}- A copy of the argument is passed to the function.
- Modifications inside the function do not affect the original variable.
- Suitable for small or simple data types where copying is inexpensive.
#include <iostream>
void modifyValue(int value) {
value = 100; // Only the local copy is modified.
}
int main() {
int a = 10;
modifyValue(a);
std::cout << "After modifyValue: " << a << std::endl; // a remains 10.
return 0;
}- An alias for the original variable is passed; no copy is made.
- Changes made within the function affect the original variable.
- Efficient for large objects and when modification of the original variable is intended.
#include <iostream>
void modifyReference(int &value) {
value = 100; // Directly modifies the original variable.
}
int main() {
int a = 10;
modifyReference(a);
std::cout << "After modifyReference: " << a << std::endl; // a becomes 100.
return 0;
}- A reference to the original variable is passed, but it is read-only.
- The function can access the variable without copying it but cannot modify it.
- Ideal for large objects when you want to avoid copying and protect against modification.
#include <iostream>
void printValue(const int &value) {
value = 100; // Error: Cannot modify a constant reference.
std::cout << "Value: " << value << std::endl;
}
int main() {
int a = 10;
printValue(a);
std::cout << "After printValue: " << a << std::endl;
return 0;
}- Reference mechanism used within C code and inherited by C++
- Allows modification of the original variable by dereferencing the pointer.
- The pointer can be
nullptr, so null checks are often necessary. - Useful passing arrays as arguments.
#include <iostream>
void modifyPointer(int *value) {
if (value != nullptr) { // Always check for nullptr.
*value = 100; // Dereference to modify the original variable.
}
}
int main() {
int a = 10;
modifyPointer(&a);
std::cout << "After modifyPointer: " << a << std::endl; // a becomes 100.
return 0;
}- Functions declared within a class are known as Member Functions (methods in C# and Java).
- Variables declared within a class are known as Member Variables or Data Members.
- Member functions and variables belonging to the class may be placed inside the
{ }.Note the semicolon;after the class's closing bracket of the class.
class NameOfClass {
int size;
void DoSomething() {
cout << "Something" << endl;
}
} obj1, obj2; // obj1 and obj2 are optional declarations of instances of NameOfClass- Alternatively,
obj1,obj2can be declared separately
class NameOfClass {
// Class members
};
NameOfClass obj1, obj2; // Declaring objects separately- Member functions may be defined outside of the class definition.
- As long as they are preceded by the class name and scope resolution operator
::
- As long as they are preceded by the class name and scope resolution operator
class NameOfClass {
// Declaration (prototype) inside the class's definition
type MemberFunctionName();
}
// Member Function definition outside class definition
type NameOfClass::MemberFunctionName() {
// Statements
}- A constructor is a special member function (method) that is automatically invoked when an object of the class is instantiated.
- It has the same name as the class.
- Equivalent to Python's
__init__()function. - Typically used to initialise member variables.
class Player {
public:
int number;
string name;
Player(int pNumber, string pName) {
number = pNumber;
name = pName;
}
};- Alternative syntax
- Instead of assigning values inside the constructor body, member variables can be directly initialised using an initialiser list.
class Player {
public:
int number;
string name;
Player(int pNumber, string pName) : number(pNumber), name(pName) {};
};- Function overloading allows multiple functions with the same name but different parameters.
- The compiler differentiates them based on the number or type of parameters.
#include <iostream>
void print(int x) {
std::cout << "Integer: " << x << std::endl;
}
void print(double x) {
std::cout << "Double: " << x << std::endl;
}
void print(std::string x) {
std::cout << "String: " << x << std::endl;
}
int main() {
print(10); // Calls print(int)
print(3.14); // Calls print(double)
print("Hello"); // Calls print(std::string)
return 0;
}- Constructors should have
publicaccess.
| Specifier | Accessible From Same Class | Accessible From Derived Class | Accessible From Outside Class |
|---|---|---|---|
| public | ✅ Yes | ✅ Yes | ✅ Yes |
| protected | ✅ Yes | ✅ Yes | ❌ No |
| private | ✅ Yes | ❌ No | ❌ No |
#include <iostream>
class Example {
public:
// Python equivalent: self.public_var
int publicVar = 1; // Accessible from anywhere
protected:
// Python equivalent: self._protected_var
int protectedVar = 2; // Accessible in derived classes
private:
// Python equivalent: self.__private_var
int privateVar = 3; // Accessible only in this class
};
class Derived : public Example {
public:
void show() {
std::cout << publicVar << std::endl; // ✅ Accessible
std::cout << protectedVar << std::endl; // ✅ Accessible
// std::cout << privateVar << std::endl; // ❌ Error: Not accessible
}
};
int main() {
Example obj;
std::cout << obj.publicVar << std::endl; // ✅ Accessible
// std::cout << obj.protectedVar << std::endl; // ❌ Error
// std::cout << obj.privateVar << std::endl; // ❌ Error
}-
Encapsulation is the practice of hiding an object's internal state (its member variables) and controlling access to that state through member functions (methods).
-
Declare member variables as
private. -
Provide getter and setter functions (or other interfaces) to manipulate and retrieve state.
-
Mutators (Setters)
- Allow changes to the object’s state.
- Typically non-
const.
-
Accessors (Getters)
- Do not modify the object’s state.
- Often declared
constto ensure they cannot alter member variables.
class Example {
private:
int data; // Encapsulated member
public:
// Setter (mutator)
void setData(int d) {
if (d > 0)
data = d;
}
// Getter (accessor)
int getData() const {
return data;
}
};- Included in C++ to provide compatibility with C.
- Similar to C++ classes, but inheritance is
publicby default (whereas for classes it'sprivateby default). - Structures in C++ can have member functions (methods), while C structures can't.
- However, a workaround in C is possible withh function pointers.
#include <stdio.h>
typedef struct {
int x, y;
void (*print)(struct Point *); // Function pointer inside the struct
} Point;
void printPoint(Point *p) {
printf("Point(%d, %d)\n", p->x, p->y);
}
int main() {
Point p = { 10, 20, printPoint }; // Assign function pointer
p.print(&p); // Call the function like a method
return 0;
}- In C++, with methods, the syntax is simplified to the following.
struct Point {
int x, y;
void print() {
cout << "Point(" << x << ", " << y << ")\n";
}
};class Example {
public:
Example() {
std::cout << "Constructor\n";
}
~Example() {
std::cout << "Destructor\n";
}
void show() {
std::cout << "Hello World!" << std::endl;
}
};- Creation: Declared normally (stack or global).
- Lifetime: Automatically managed; destroyed at scope exit.
- Method invocation using operator
.
int main() {
Example obj; // Creation (static)
obj.show(); // Method invocation using '.'
// Destructor called automatically
return 0;
}- Creation: Allocated using
new(heap). - Lifetime: Managed manually; must be deallocated using
delete. - Method invocation using dereference operator
-> - Useful when object lifetime needs to extend beyond scope.
int main() {
Example* obj = new Example; // Creation (dynamic)
obj->show(); // Method invocation using '->'
delete obj; // Manual deallocation (calls destructor)
return 0;
}- Create objects on the fly without storing them in a named variable.
- Typically used for immediate function calls.
#include <iostream>
class Example {
public:
Example() {
std::cout << "Constructor\n";
}
~Example() {
std::cout << "Destructor\n";
}
void show() {
std::cout << "Hello World!" << std::endl;
}
};
// Function that takes a pointer to an Example object.
void display(Example *ex) {
if (ex) {
ex->show();
delete ex; // Clean up the dynamically allocated object.
}
}
int main() {
Example().show(); // Create an anonymous object (static) and call its method immediately.
display(new Example); // Create an anonymous (dynamic) object and pass its pointer to the function.
// Output
// Constructor
// Hello World!
// Destructor
// Constructor
// Hello World!
// Destructor
return 0;
}- Syntax: Use a colon (
:) after the class name followed by the access specifier (public,protected,private) and the base class name. - Multiple inheritance (a class can inherit from more than one base class) is possible in C++, but its use is not recommended.
- Naming Conflicts: Same member names in different bases can cause ambiguity.
- Diamond Problem: Ambiguity arises when two base classes inherit from the same ancestor.
class Base {
public:
void show() {
std::cout << "Base\n";
}
};
class Derived : public Base {
// Inherits public members of Base as public
};
-
Abstract Member Function (virtual):
- Declared with
= 0(pure virtual function). - No implementation in the base class.
- Forces derived classes to provide an override.
- Declared with
-
Abstract Class:
- Contains at least one pure virtual function.
- Cannot be instantiated directly.
- Serve as a blueprint for derived classes.
- Purpose: Enforce that certain methods are implemented in derived classes.
class AbstractBase {
public:
virtual void print() = 0; // Pure virtual function
};
class Concrete : public AbstractBase {
private:
int id;
public:
Concrete(int id)
: id(id) {
}
void print() override { // 'override' is optional but good practice (clarity)
std::cout << "Overriden function" << std::endl;
}
};
int main() {
Concrete c(1);
c.print(); // Calls the implemented function
return 0;
}- Header file (
.h): Contains the class definition (the interface) and method declarations. - Source file (
.cpp): Contains the implementation (method definitions). - Maintainability: Changes to implementation don’t force recompilation of all files that include the header (when used properly).
- File Naming: By convention, each class has its own
.hand.cppfile, named after the class (e.g.,Player.handPlayer.cpp). - See
03_nicholas_day_corurse/project_organisationfor an example
#include <iostream>
class Base {
private:
int id;
public:
Base() {
std::cout << "Base Default Constructor\n";
}
Base(int id) : id(id) {
std::cout << "Base Non Default Constructor, id = " << id << std::endl;
};
};-
When a derived class is instantiated, its constructor implicitly calls the default constructor of the base class.
-
The base class constructor executes first, followed by the derived class constructor.
-
In this example, the default constructor of the base class is called first.
class Derived : public Base { public: Derived() { std::cout << "Derived Default Constructor\n"; } Derived(int id) { std::cout << "Derived Non Default Constructor, id = " << id << std::endl; }; }; int main() { Derived car(3); // Base class constructor runs first, then derived class return 0; // Output: // Base Default Constructor // Derived Non Default Constructor, id = 3 }
-
To call the non default constructor, (and thus assign a value to the private parameter id).
- The parent's constructor name follows the colon
:after the child's constructor.
- The parent's constructor name follows the colon
-
Arguments can be passed this way from the child to the parent's constructor.
class Derived : public Base { public: Derived() { std::cout << "Derived Default Constructor\n"; } Derived(int id) : Base(id) { std::cout << "Derived Non Default Constructor, id = " << id << std::endl; }; }; int main() { Derived car(3); // Base class constructor runs first, then derived class return 0; // Output: // Base Non Default Constructor, id = 3 // Derived Non Default Constructor, id = 3 }
- In Python, the same effect can be achieved using
super(), which calls the constructor of the base class. - However, if the class has a base class with a constructor, Python does NOT automatically call it unless you explicitly do so using
super().__init__()in the derived class.class Base: def __init__(self, id): self.id = id print(f"Base Non-Default Constructor, id = {id}") class Derived(Base): def __init__(self, id): super().__init__(id) # Calls the constructor of the base class print(f"Derived Non-Default Constructor, id = {id}") obj = Derived(3) # Output # Base Non-Default Constructor, id = 3 # Derived Non-Default Constructor, id = 3
- In Python, the same effect can be achieved using
-
thisis a pointer to the current object in a class -
thisis implicitly passed to all non-static member function -
It's however helpful for disambiguating member variables and parameters
-
Equivalent to Python's
self- If the constructor parameter has a different name than the class member, values can be assigned direclty:
class MyClass { private: int value; public: MyClass(int v) { // Different parameter name value = v; // No need for this-> (no ambiguity) } };
- If the parameter name is the same as the member variable,
this->is required:
class MyClass { private: int value; public: MyClass(int value) { this->value = value; // Differentiates between parameter and instance variable } };
- Method chaining
class MyClass { private: int value; public: MyClass &setValue(int value) { // &: return a **reference** to the object this->value = value; return *this; // Returns current object (not pointer) } }; int main() { MyClass obj; obj.setValue(10).setValue(20); // Chained calls }
-
Analogously, in a C fashion, we could return the actual
thispointerclass MyClass { private: int value; public: MyClass* setValue(int value) { this->value = value; return this; // Returns pointer to the current object } }; int main() { MyClass obj; obj.setValue(10)->setValue(20); // Need to use -> instead of . }