C++ Notes

Function Overloading and C++ Classes

Function Overloading

Function overloading allows multiple functions with the same name but different parameters to exist in the same scope. This is a feature of C++ and some other statically-typed languages.

In C

Function overloading is not possible in C. However, it can be somewhat emulated using variable arguments. The printf function is a classic example:

int printf(const char *format, ...);

In Python

Python variables are dynamically typed, which means a single function can handle multiple types:

def foo(param):
    if isinstance(param, int):
        # Do something with int param
        return int_result
    elif isinstance(param, float):
        # Do something with float param
        return float_result
    elif isinstance(param, str):
        # Do something with string param
        return str_result

In C++

Since C++ is statically typed, multiple functions need to be written to handle different types:

// Compiler will call this one if foo has an int argument
int foo(int param){
    // Do something with int param
    return int_result;
}

// Compiler will call this one if foo has a float argument
float foo(float param){
    // Do something with float param
    return float_result;
}

C++ Classes

In C++, classes are similar to structs, but with different default access levels for members.

Access Specifiers

C++ provides three access specifiers:

Access Example

struct Name{
    int PubDataMember;  // Default is public
    private:  // All members are private after
        int PrivDataMember;
    protected:  // Now all are protected after
        int ProtDataMember;
    public:  // Now all are public again after
        int OthPubDataMember;
};

class OtherName{
    int PrivDataMember;  // Default is private
    public:  // All members are public after
        int PubDataMember;
    protected:  // Now all are protected after
        int ProtDataMember;
    private:  // Now all are private again after
        int OthPrivDataMember;
};

Destructors

Destructors are member functions that are called when an object is destroyed. They are useful for cleanup tasks.

Destructor Example

// Typically in header file
class Name{
    public:
        Name();  // Constructor
        ~Name(); // Destructor
};

// Typically in cpp file
Name::Name(){
    std::cout << "Name constructor" << std::endl;
}

Name::~Name(){
    std::cout << "Name destructor" << std::endl;
}

// Typically in another cpp file
{
    std::cout << "Start" << std::endl;
    Name Object; // Constructor called here
} // Object goes out of scope and destructor called
std::cout << "End" << std::endl;

// Output of above code
// Start
// Name constructor
// Name destructor
// End

Inheritance

Inheritance allows a class or struct to be derived from another, enabling code reuse and polymorphism.

In Python

class Base:
    def __init__(self, param):
        self.DataMember = param

class Derived(Base):
    def __init__(self, param, param2):
        Base.__init__(self, param)
        self.DataMember2 = param2

In C++

// Typically in header file
struct Base{
    int DataMember;
    Base(int param); // Constructor
};

struct Derived : Base{
    int DataMember2;
    Derived(int param, int param2); // Constructor
};

// Typically in cpp file
Base::Base(int param){
    DataMember = param;
}

Derived::Derived(int param, int param2) : Base(param){
    DataMember2 = param2;
}

Function Overriding

Function overriding allows a derived class to provide a specific implementation of a function that is already defined in its base class.

C++ Example

// Typically in header file
struct Base{
    virtual void foo(int param);
};

struct Derived : Base{
    void foo(int param) override;
};

// Typically in cpp file
void Base::foo(int param){
    std::cout << "Base " << param << std::endl;
}

void Derived::foo(int param){
    std::cout << "Derived " << param << std::endl;
}

// Typically in another cpp file
Derived Object;
Base *Ptr = &Object; // Point to Object
Object.foo(3); // Calls overridden function
Ptr->foo(4);   // Calls function of object type

// Output of above code
// Derived 3
// Derived 4

Interfaces

Interfaces define the allowed interaction and expected behavior, but not the implementation. In C++, abstract classes are used for interfaces.

C++ Example

// Typically in header file
class Logger{
    public:
        virtual ~Logger(){}; // Good practice
        virtual void log(const std::string &mes) = 0;
};

// Typically in another header file
class StdLogger : public Logger{
    public:
        void log(const std::string &mes) override;
};

// Typically in yet another header file
class FileLogger : public Logger{
    std::ofstream DFile;
    public:
        FileLogger(const std::string &filename);
        void log(const std::string &mes) override;
};

// Typically in cpp file
void StdLogger::log(const std::string &mes){
    std::cout << mes << std::endl;
}

// Typically in another cpp file
FileLogger::FileLogger(const std::string &filename) : DFile(filename){}

void FileLogger::log(const std::string &mes){
    DFile << mes << std::endl;
}

// Typically in yet another cpp file
void foo(Logger *logger){
    logger->log("A message");
}

Exercises and Exam-Style Questions

  1. Explain the difference between function overloading and function overriding in C++.
  2. Write a simple C++ program that demonstrates function overloading with three functions named add that handle int, float, and double types.
  3. Consider the following code snippet. What will be the output and why?
    
    	class Base {
                public:
                    virtual void show() { std::cout << "Base class" << std::endl; }
            };
    
            class Derived : public Base {
                public:
                    void show() override { std::cout << "Derived class" << std::endl; }
            };
    
            int main() {
                Base *b;
                Derived d;
                b = &d;
                b->show();
                return 0;
            }
            
  4. Write a C++ class Shape with a virtual function area(). Derive two classes Circle and Rectangle from Shape and override the area() function. Demonstrate polymorphism by calling the area() function using a pointer to Shape.
  5. What are the benefits of using interfaces in C++? Provide an example where interfaces might be particularly useful.