Skip to main content
PProfileMatch

C++ Classes, Objects and std::vector

ProgrammingLesson 6 of 65 min read

What you'll learn

  • Define a class with private data, a constructor and member functions
  • Explain the difference between struct and class and what public and private do
  • Use std::vector to add, access, remove, sort and iterate over elements
  • Combine classes and vectors to model a small real-world problem

On this page

  1. Defining a class
  2. struct vs class
  3. std::vector basics
  4. Putting it together: a gradebook
  5. Common mistakes
  6. How this shows up in interviews and real projects

Real programs are about things: bank accounts, students, products. A class lets you define such a thing once, with its data and behavior bundled together, while std::vector lets you hold as many of them as you need. Together they form the backbone of most everyday C++ code. This lesson builds both up and finishes with a small gradebook that uses them together.

Defining a class

A class bundles data members with member functions. Access specifiers control who can use them: public members form the interface other code uses, and private members are hidden implementation details. Hiding the data (encapsulation) lets the class enforce its own rules. In the example, nobody outside can set a negative balance because the balance can only change through deposit and withdraw.

A constructor has the same name as the class, no return type, and runs when an object is created. The part after the colon, : owner_(owner), balance_(openingBalance), is the member initialiser list, the idiomatic way to set members. Functions marked const after their parameter list promise not to change the object, so they can be called on const objects. Note the semicolon after the closing brace of a class, which beginners often forget.

bank_account.cpp
#include <iostream>
#include <string>
using namespace std;

class BankAccount {
public:
    BankAccount(string owner, double openingBalance = 0)
        : owner_(owner), balance_(openingBalance) {}

    void deposit(double amount) {
        if (amount <= 0) {
            cout << "Deposit must be positive" << endl;
            return;
        }
        balance_ += amount;
    }

    bool withdraw(double amount) {
        if (amount > balance_) {
            return false;
        }
        balance_ -= amount;
        return true;
    }

    double balance() const { return balance_; }
    const string& owner() const { return owner_; }

private:
    string owner_;
    double balance_;
};

int main() {
    BankAccount acc("Rohan", 1000);
    acc.deposit(500);
    acc.deposit(-20);
    cout << boolalpha << acc.withdraw(2000) << endl;
    cout << acc.withdraw(300) << endl;
    cout << acc.owner() << " has " << acc.balance() << endl;

    BankAccount empty("Meera");
    cout << empty.balance() << endl;

    BankAccount copy = acc;
    copy.deposit(100);
    cout << acc.balance() << endl;
    cout << copy.balance() << endl;
    return 0;
}

Output

Deposit must be positive
false
true
Rohan has 1200
0
1200
1300

Trace it: Rohan opens with 1000, deposits 500 (1500), the negative deposit is rejected, the 2000 withdrawal fails, and the 300 withdrawal succeeds, leaving 1200. Meera's account uses the default opening balance of 0. The last lines show an important C++ difference from Java: BankAccount copy = acc; makes a real, independent copy of the object, so depositing into copy does not change acc. C++ objects have value semantics by default. To share one object instead, you would use a reference or pointer.

struct vs class

In C++ the only technical difference between struct and class is the default access: members of a struct are public by default, while members of a class are private by default. By convention, use struct for simple bundles of data with no rules to enforce (a point, a product record) and class when you have invariants to protect and member functions that maintain them. You will see both in the next program.

std::vector basics

std::vector<T> is a resizable array that owns its elements and cleans them up automatically, so it should be your default container. Include <vector>, and the element type goes in angle brackets: vector<string> is a list of strings. Elements sit side by side in memory, so access by index is fast, and adding at the end is cheap on average. Inserting or erasing in the middle is slower because later elements must shift.

vector_basics.cpp
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using namespace std;

struct Product {
    string name;
    double price;
    int quantity;
};

int main() {
    vector<string> cart;
    cart.push_back("Keyboard");
    cart.push_back("Mouse");
    cart.push_back("Monitor");
    cart.insert(cart.begin() + 1, "USB Hub");
    cout << cart.size() << " items, first: " << cart[0] << ", last: " << cart.back() << endl;

    cart.erase(cart.begin() + 2);
    cart.pop_back();
    for (const string& item : cart) {
        cout << "- " << item << endl;
    }

    vector<int> scores = {55, 80, 92, 38};
    sort(scores.begin(), scores.end());
    for (size_t i = 0; i < scores.size(); i++) {
        cout << scores[i] << (i + 1 < scores.size() ? ", " : "\n");
    }

    Product laptop{"Laptop", 55000.0, 2};
    cout << laptop.name << " x" << laptop.quantity << " = " << laptop.price * laptop.quantity << endl;
    return 0;
}

Output

4 items, first: Keyboard, last: Monitor
- Keyboard
- USB Hub
38, 55, 80, 92
Laptop x2 = 110000

Follow the cart: after three push_back calls and the insert at position 1 it holds Keyboard, USB Hub, Mouse, Monitor. erase(cart.begin() + 2) removes Mouse, and pop_back() removes Monitor, leaving two items. Positions are given as iterators such as begin() + 2, not as plain numbers. sort from <algorithm> rearranges the vector in place into ascending order.

Putting it together: a gradebook

This program stores students in a vector<Student>. Each Student is a class that holds its own vector<int> of marks and computes its average. It uses a range-based loop with const Student& to print without copying, and an index loop to find the top scorer.

gradebook.cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;

class Student {
public:
    Student(string name, vector<int> marks)
        : name_(name), marks_(marks) {}

    double average() const {
        if (marks_.empty()) {
            return 0;
        }
        int sum = 0;
        for (int m : marks_) {
            sum += m;
        }
        return static_cast<double>(sum) / marks_.size();
    }

    bool isPassing() const { return average() >= 40; }
    const string& name() const { return name_; }

private:
    string name_;
    vector<int> marks_;
};

int main() {
    vector<Student> students;
    students.push_back(Student("Asha", {80, 90, 75}));
    students.push_back(Student("Ravi", {35, 30, 41}));
    students.push_back(Student("Meera", {95, 85, 90}));

    for (const Student& s : students) {
        cout << s.name() << ": " << s.average()
             << (s.isPassing() ? " (pass)" : " (fail)") << endl;
    }

    size_t best = 0;
    for (size_t i = 1; i < students.size(); i++) {
        if (students[i].average() > students[best].average()) {
            best = i;
        }
    }
    cout << "Top: " << students[best].name() << endl;
    return 0;
}

Output

Asha: 81.6667 (pass)
Ravi: 35.3333 (fail)
Meera: 90 (pass)
Top: Meera

Asha's marks sum to 245, and 245 divided by 3 is 81.666..., which cout shows as 81.6667 with its default 6 significant digits. Meera's average is exactly 90, so it prints 90. Because average() and name() are const, they can be called through the const Student& loop variable. Remove the const from average() and the program stops compiling, which is exactly the protection const is for.

Common mistakes

  • Forgetting the semicolon after a class. The compiler reports error: expected ';' after class definition (older versions say expected ';' after class).
  • Accessing a private member. acc.balance_ from main fails with error: 'double BankAccount::balance_' is private within this context. Add a public accessor instead.
  • Missing `const` on a getter. Calling a non-const function through a const& gives error: passing 'const Student' as 'this' argument discards qualifiers.
  • Indexing past the end of a vector. cart[10] on a four-element vector is undefined behavior with no error, whereas cart.at(10) throws std::out_of_range.
  • Modifying a vector while iterating it. Calling push_back or erase inside a range-based loop over the same vector can invalidate the loop's iterators, which is undefined behavior. Collect changes first or use the erase-remove idiom.
  • Forgetting the vector include or element type. vector<int> v; without #include <vector> fails with error: 'vector' was not declared in this scope.

How this shows up in interviews and real projects

Interviews ask about the difference between struct and class, what a constructor initialiser list is, the rule of three/five and why vector is the default container. Most array and string problems you will meet are solved with std::vector, std::string and the algorithms in <algorithm>, so being fluent here pays off directly. Work through the practice plan in coding interview prep for self-taught developers, and explore open roles to see where C++ is in demand.

Try it yourself

Create a Product class with private name, price and stock members, a constructor, and methods sell(int qty) (return false if there is not enough stock) and isLowStock(int threshold) const. Store several products in a vector<Product>, sell some units, and print the names of low-stock products.

Show a hint

Loop with for (const Product& p : products) for printing, and use a non-const reference (for (Product& p : products)) when you need to call sell.

Key takeaways

  • A class bundles data and functions; use private for data and public for the interface, and end the class with a semicolon.
  • struct and class differ only in default access (public versus private).
  • Constructors with member initialiser lists set up objects, and const member functions promise not to modify them.
  • C++ objects are copied by default (value semantics), unlike Java's object references.
  • std::vector is the default dynamic array: push_back, size, at, erase and range-based loops cover most uses.

Test yourself

A trick from this lesson's ride: struct and class differ in one default: struct leaves the door open, class locks it. Four more tricks like it wait along the route, each followed by a question.

Ride this lesson

Frequently asked questions

What is the difference between struct and class in C++?

Their only technical difference is default access: struct members are public by default and class members are private by default. By convention, struct is used for simple data holders and class for types that hide their data and enforce rules.

What is std::vector in C++ and when should I use it?

std::vector is a resizable array that manages its own memory. It is the default container in C++ because it is fast to index and iterate, grows automatically with push_back, and frees itself when it goes out of scope. Use it instead of raw arrays in almost all new code.

What does a constructor initializer list do in C++?

The initializer list after a constructor's parameters, such as : owner_(owner), balance_(openingBalance), initialises members directly when the object is created, rather than assigning to them afterwards. It is required for const and reference members and is generally more efficient and idiomatic.