In the previous chapter, we looked at Object Oriented Programming from a conceptual level, understanding why it exists and briefly touching on classes and objects along with the four core pillars. Now it is time to get hands-on and explore classes and objects properly, since almost everything else in object-oriented C++ is built directly on top of these two ideas.
A class is the actual code construct in C++ used to define a new custom data type. It describes what kind of data a group of related objects will hold, and what actions those objects will be able to perform. Once a class is defined, it does not occupy any memory by itself, since it is simply a description. Memory is only reserved once an object, meaning a real instance of that class, is actually created.
In this tutorial, you will learn how to properly define a class, how access specifiers control visibility of data and functions, how member functions work, how constructors and destructors manage an object's life cycle, and how the special this pointer is used inside a class.
A class definition begins with the class keyword, followed by the class name, and a body enclosed in curly braces that lists the data members and member functions belonging to that class. A semicolon is always required after the closing brace, which is a detail beginners often forget.
class ClassName {
// data members
// member functions
};
#include <iostream>
using namespace std;
class Video {
public:
string title;
int views;
void display() {
cout << "Title: " << title << ", Views: " << views << endl;
}
};
int main() {
string channel = "CS Engineering Gyan";
Video lecture;
lecture.title = "Classes and Objects";
lecture.views = 2700;
cout << channel << " new video:" << endl;
lecture.display();
return 0;
}
CS Engineering Gyan new video: Title: Classes and Objects, Views: 2700
Here, Video is a class containing two data members, title and views, along with a member function called display. The object lecture is created from this class and used to store real data specific to one particular video.
Once a class has been defined, you can create as many objects from it as needed, and each object maintains its own independent copy of the class's data members, even though they all share the same member functions.
#include <iostream>
using namespace std;
class Video {
public:
string title;
int views;
};
int main() {
string channel = "CS Engineering Gyan";
Video video1;
video1.title = "Pointers in C++";
video1.views = 5200;
Video video2;
video2.title = "Object Oriented Programming";
video2.views = 3400;
cout << channel << " video 1: " << video1.title << " (" << video1.views << " views)" << endl;
cout << channel << " video 2: " << video2.title << " (" << video2.views << " views)" << endl;
return 0;
}
CS Engineering Gyan video 1: Pointers in C++ (5200 views) CS Engineering Gyan video 2: Object Oriented Programming (3400 views)
Even though video1 and video2 are both created from the same Video class, they hold completely separate data. Changing the title or views of one object has no effect whatsoever on the other.
Access specifiers control which parts of a class can be accessed from outside the class itself. C++ provides three access specifiers, and understanding the difference between them is essential for applying encapsulation properly.
| Access Specifier | Description |
|---|---|
| public | Members can be accessed directly from outside the class. |
| private | Members can only be accessed from within the class itself, by default. |
| protected | Members can be accessed within the class and by classes that inherit from it. |
#include <iostream>
using namespace std;
class Channel {
private:
int subscriberCount;
public:
void setSubscribers(int count) {
subscriberCount = count;
}
int getSubscribers() {
return subscriberCount;
}
};
int main() {
string channelName = "CS Engineering Gyan";
Channel csEngineeringGyan;
csEngineeringGyan.setSubscribers(60000);
cout << channelName << " subscribers: " << csEngineeringGyan.getSubscribers() << endl;
return 0;
}
CS Engineering Gyan subscribers: 60000
Since subscriberCount is private, it cannot be accessed directly using something like csEngineeringGyan.subscriberCount from outside the class. Instead, the public functions setSubscribers and getSubscribers act as controlled entry points, which is a practical demonstration of encapsulation.
It is worth noting that by default, class members in C++ are private if no access specifier is mentioned, unlike structures, where members default to public. This is one of the small but important differences between the class and struct keywords in C++.
A member function is simply a function that is defined inside a class and operates on the data members of that class. Member functions can either be defined directly inside the class body, or declared inside the class and defined separately outside of it using the scope resolution operator.
#include <iostream>
using namespace std;
class Playlist {
public:
string name;
int videoCount;
void showDetails();
};
void Playlist::showDetails() {
cout << "Playlist: " << name << ", Videos: " << videoCount << endl;
}
int main() {
string channel = "CS Engineering Gyan";
Playlist dsaPlaylist;
dsaPlaylist.name = "DSA Series";
dsaPlaylist.videoCount = 25;
cout << channel << " playlist update:" << endl;
dsaPlaylist.showDetails();
return 0;
}
CS Engineering Gyan playlist update: Playlist: DSA Series, Videos: 25
The scope resolution operator, written as two colons, tells the compiler that the showDetails function being defined outside the class body actually belongs to the Playlist class. This style is common in larger projects, where class declarations are kept in one file and their implementations in another.
A constructor is a special member function that runs automatically whenever an object of a class is created. Constructors are typically used to initialize the data members of an object with meaningful starting values, removing the need to set them manually every single time.
A constructor always shares the exact same name as the class itself, and it never has a return type, not even void.
#include <iostream>
using namespace std;
class Video {
public:
string title;
int views;
Video(string videoTitle, int initialViews) {
title = videoTitle;
views = initialViews;
cout << "New video object created" << endl;
}
};
int main() {
string channel = "CS Engineering Gyan";
Video newUpload("Classes and Objects", 100);
cout << channel << " uploaded: " << newUpload.title << " with " << newUpload.views << " views" << endl;
return 0;
}
New video object created CS Engineering Gyan uploaded: Classes and Objects with 100 views
This particular constructor is called a parameterized constructor, since it accepts arguments and uses them to set up the object's initial state at the moment of creation. C++ also allows a default constructor, which takes no parameters at all, and is automatically provided by the compiler if no constructor is written manually.
#include <iostream>
using namespace std;
class Comment {
public:
Comment() {
cout << "A new comment object has been created" << endl;
}
};
int main() {
string channel = "CS Engineering Gyan";
cout << channel << " comment section activity:" << endl;
Comment newComment;
return 0;
}
CS Engineering Gyan comment section activity: A new comment object has been created
A destructor is another special member function that is automatically called when an object goes out of scope or is explicitly deleted, and it is typically used to perform cleanup tasks before the object is destroyed, such as releasing dynamically allocated memory.
A destructor shares the class name preceded by a tilde symbol, takes no parameters, and cannot be overloaded, meaning a class can have only one destructor.
#include <iostream>
using namespace std;
class Session {
public:
Session() {
cout << "Viewer session started" << endl;
}
~Session() {
cout << "Viewer session ended" << endl;
}
};
int main() {
string channel = "CS Engineering Gyan";
cout << channel << " tracking a viewer session:" << endl;
Session viewerSession;
cout << "Viewer is watching the video" << endl;
return 0;
}
CS Engineering Gyan tracking a viewer session: Viewer session started Viewer is watching the video Viewer session ended
Notice that the destructor runs automatically right before the program ends, at the point where viewerSession goes out of scope, without needing to be called explicitly anywhere in the code.
Inside any non-static member function, C++ automatically provides a special pointer called this, which points to the specific object that the member function is currently operating on. This becomes especially useful when a constructor's parameter name is the same as a data member's name.
#include <iostream>
using namespace std;
class Channel {
public:
string name;
Channel(string name) {
this->name = name;
}
void showName() {
cout << "Channel name: " << this->name << endl;
}
};
int main() {
Channel csEngineeringGyan("CS Engineering Gyan");
csEngineeringGyan.showName();
return 0;
}
Channel name: CS Engineering Gyan
Here, the constructor parameter is also named name, which matches the data member's name exactly. Writing this->name makes it clear that we are referring to the object's own data member, rather than the local parameter, resolving what would otherwise be a naming conflict.
Just like ordinary variables, objects can also be passed to functions, either by value, where a copy of the object is made, or by reference, where the function operates directly on the original object.
#include <iostream>
using namespace std;
class Video {
public:
string title;
int likes;
};
void addBonusLikes(Video& v) {
v.likes += 50;
}
int main() {
string channel = "CS Engineering Gyan";
Video featuredVideo;
featuredVideo.title = "Constructors Explained";
featuredVideo.likes = 300;
addBonusLikes(featuredVideo);
cout << channel << " " << featuredVideo.title << " likes after bonus: " << featuredVideo.likes << endl;
return 0;
}
CS Engineering Gyan Constructors Explained likes after bonus: 350
Because the function accepts the object by reference, using an ampersand in the parameter type, any change made inside the function is reflected in the original object back in main, rather than being lost on a temporary copy.
| Advantages | Limitations |
|---|---|
| Group related data and behavior into a single, organized unit. | Overusing classes for very simple tasks can add unnecessary structure. |
| Constructors and destructors automate setup and cleanup work. | Poorly designed constructors can make objects harder to create correctly. |
| Access specifiers protect internal data from unwanted external changes. | Beginners may find access specifier rules confusing at first. |
| Mistake | Correct Practice |
|---|---|
| Forgetting the semicolon after the closing brace of a class definition. | Always end a class definition with a semicolon. |
| Trying to access private members directly from outside the class. | Use public member functions to interact with private data. |
| Giving a constructor a return type by mistake. | Remember that constructors never have a return type, not even void. |
| Assuming a struct behaves exactly like a class in every way. | Remember that struct members are public by default, while class members are private by default. |
Classes and objects form the practical foundation of object-oriented programming in C++, turning the conceptual ideas discussed earlier into working code. A class defines the structure, while objects are the actual instances that carry real data and can be created, used, and destroyed throughout a program's execution.
Access specifiers, member functions, constructors, and destructors all work together to give a class full control over how its data is initialized, accessed, and cleaned up. The this pointer adds one more layer of clarity, letting a member function refer confidently to the exact object it is currently working with.
With a solid grasp of classes and objects, you are now ready to move on to inheritance in C++, where existing classes can be extended and specialized to build more powerful and reusable object hierarchies.