Skip to content
Laviyx Learn

Try

C++ Object-Oriented Programming Practice

Practise C++ classes, constructors, encapsulation, inheritance, virtual functions, abstract classes and operator overloading.

Intermediate6 problemsUpdated

Review first: Classes and Objects, Constructors and Destructors, Encapsulation, Inheritance, Polymorphism and Virtual Functions, Abstract Classes and Interfaces, Operator Overloading

  1. 1.A bank account class

    Intermediate

    Write a class BankAccount with a private balance. Provide deposit(double), bool withdraw(double) (which refuses amounts above the balance) and balance() const. Deposit 1000, withdraw 300, then try to withdraw 5000.

    Show hint

    Keep the balance private so it can only change through methods that check the rules.

    Show solution
    C++
    #include <iostream>
    
    class BankAccount {
    public:
        void deposit(double amount) {
            if (amount > 0) balance_ += amount;
        }
    
        bool withdraw(double amount) {
            if (amount <= 0 || amount > balance_) return false;
            balance_ -= amount;
            return true;
        }
    
        double balance() const { return balance_; }
    
    private:
        double balance_ = 0;
    };
    
    int main() {
        BankAccount acc;
        acc.deposit(1000);
        std::cout << std::boolalpha;
        std::cout << acc.withdraw(300) << ' ' << acc.withdraw(5000) << '\n';
        std::cout << acc.balance() << '\n';
    }
    Output
    true false
    700
  2. 2.Constructor with validation

    Intermediate

    Write a class Rectangle whose constructor takes a width and height and throws std::invalid_argument if either is not positive. Add area() and perimeter(). Create a 4 × 2.5 rectangle, print both values, then try to create a 0 × 3 rectangle.

    Show hint

    Initialize members in the initializer list, and validate in the constructor body.

    Show solution
    C++
    #include <iostream>
    #include <stdexcept>
    
    class Rectangle {
    public:
        Rectangle(double width, double height) : width_(width), height_(height) {
            if (width <= 0 || height <= 0) {
                throw std::invalid_argument("sides must be positive");
            }
        }
    
        double area() const { return width_ * height_; }
        double perimeter() const { return 2 * (width_ + height_); }
    
    private:
        double width_;
        double height_;
    };
    
    int main() {
        Rectangle r(4, 2.5);
        std::cout << r.area() << ' ' << r.perimeter() << '\n';
    
        try {
            Rectangle bad(0, 3);
        } catch (const std::invalid_argument& e) {
            std::cout << "Error: " << e.what() << '\n';
        }
    }
    Output
    10 13
    Error: sides must be positive
  3. 3.Inheritance and overriding

    Intermediate

    Create a base class Employee with a name, a salary and a virtual double pay() const. Derive Manager, which adds a bonus to the pay. Print the pay of an employee earning 50000 and a manager earning 80000 with a 15000 bonus, both through const Employee&.

    Show hint

    Call the base constructor from the derived initializer list, and reuse Employee::pay() inside Manager::pay().

    Show solution
    C++
    #include <iostream>
    #include <string>
    
    class Employee {
    public:
        Employee(std::string name, double salary) : name_(std::move(name)), salary_(salary) {}
        virtual ~Employee() = default;
        virtual double pay() const { return salary_; }
        const std::string& name() const { return name_; }
    
    private:
        std::string name_;
        double salary_;
    };
    
    class Manager : public Employee {
    public:
        Manager(std::string name, double salary, double bonus)
            : Employee(std::move(name), salary), bonus_(bonus) {}
        double pay() const override { return Employee::pay() + bonus_; }
    
    private:
        double bonus_;
    };
    
    void print_pay(const Employee& e) {
        std::cout << e.name() << ": " << e.pay() << '\n';
    }
    
    int main() {
        Employee e("Ravi", 50000);
        Manager m("Meera", 80000, 15000);
        print_pay(e);
        print_pay(m);
    }
    Output
    Ravi: 50000
    Meera: 95000
  4. 4.An abstract Shape

    Intermediate

    Define an abstract class Shape with pure virtual area() and name(). Implement Circle (use 3.14159) and Square. Store a circle of radius 1 and a square of side 3 in a std::vector<std::unique_ptr<Shape>>, and print each name and area, then the total.

    Show hint

    virtual double area() const = 0; makes a function pure virtual. Give Shape a virtual destructor.

    Show solution
    C++
    #include <iostream>
    #include <memory>
    #include <string>
    #include <vector>
    
    class Shape {
    public:
        virtual ~Shape() = default;
        virtual double area() const = 0;
        virtual std::string name() const = 0;
    };
    
    class Circle : public Shape {
    public:
        explicit Circle(double r) : r_(r) {}
        double area() const override { return 3.14159 * r_ * r_; }
        std::string name() const override { return "Circle"; }
    
    private:
        double r_;
    };
    
    class Square : public Shape {
    public:
        explicit Square(double s) : s_(s) {}
        double area() const override { return s_ * s_; }
        std::string name() const override { return "Square"; }
    
    private:
        double s_;
    };
    
    int main() {
        std::vector<std::unique_ptr<Shape>> shapes;
        shapes.push_back(std::make_unique<Circle>(1));
        shapes.push_back(std::make_unique<Square>(3));
    
        double total = 0;
        for (const auto& s : shapes) {
            std::cout << s->name() << ": " << s->area() << '\n';
            total += s->area();
        }
        std::cout << "Total: " << total << '\n';
    }
    Output
    Circle: 3.14159
    Square: 9
    Total: 12.1416
  5. 5.Operator overloading

    Intermediate

    Write struct Vec2 { double x; double y; }; with operator+, operator== and operator<< (printing (x, y)). Print Vec2{1, 2} + Vec2{3, 4} and whether Vec2{1, 2} == Vec2{1, 2}.

    Show hint

    operator<< takes std::ostream& and the vector, and returns the stream.

    Show solution
    C++
    #include <iostream>
    
    struct Vec2 {
        double x = 0;
        double y = 0;
    };
    
    Vec2 operator+(const Vec2& a, const Vec2& b) {
        return {a.x + b.x, a.y + b.y};
    }
    
    bool operator==(const Vec2& a, const Vec2& b) {
        return a.x == b.x && a.y == b.y;
    }
    
    std::ostream& operator<<(std::ostream& out, const Vec2& v) {
        return out << '(' << v.x << ", " << v.y << ')';
    }
    
    int main() {
        std::cout << Vec2{1, 2} + Vec2{3, 4} << '\n';
        std::cout << std::boolalpha << (Vec2{1, 2} == Vec2{1, 2}) << '\n';
    }
    Output
    (4, 6)
    true
  6. 6.Construction and destruction order

    Intermediate

    Write a class Tracer that prints start NAME in its constructor and end NAME in its destructor. In main, create Tracer a("outer"), then a block containing Tracer b("inner"), then print back in main. Predict the output first.

    Show hint

    Objects are destroyed at the end of their scope, in the reverse order of creation.

    Show solution
    C++
    #include <iostream>
    #include <string>
    
    class Tracer {
    public:
        explicit Tracer(std::string name) : name_(std::move(name)) {
            std::cout << "start " << name_ << '\n';
        }
        ~Tracer() { std::cout << "end " << name_ << '\n'; }
    
    private:
        std::string name_;
    };
    
    int main() {
        Tracer a("outer");
        {
            Tracer b("inner");
        }
        std::cout << "back in main\n";
    }
    Output
    start outer
    start inner
    end inner
    back in main
    end outer