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
structandclassand whatpublicandprivatedo - Use
std::vectorto add, access, remove, sort and iterate over elements - Combine classes and vectors to model a small real-world problem
On this page
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.
#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.
#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.
#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 sayexpected ';' after class). - Accessing a private member.
acc.balance_frommainfails witherror: '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&giveserror: 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, whereascart.at(10)throwsstd::out_of_range. - Modifying a vector while iterating it. Calling
push_backoreraseinside 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 witherror: '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
privatefor data andpublicfor the interface, and end the class with a semicolon. structandclassdiffer only in default access (public versus private).- Constructors with member initialiser lists set up objects, and
constmember functions promise not to modify them. - C++ objects are copied by default (value semantics), unlike Java's object references.
std::vectoris the default dynamic array:push_back,size,at,eraseand 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.
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.