30% complete
Classes and Objects in C++
Classes and objects are the cornerstone of Object-Oriented Programming (OOP) in C++. They enable you to create complex programs by modeling real-world entities as objects with properties (data members) and behaviors (member functions).
What You'll Learn
- How to define classes and create objects in C++
- How to work with constructors and destructors
- Understanding access modifiers (public, private, protected)
- Implementing encapsulation and data hiding
- Working with static members and friend functions
- Best practices for designing classes
Introduction to Classes and Objects
In C++, a class is a user-defined data type that serves as a blueprint for objects. It defines the properties (data members) and behaviors (member functions) that the objects of the class will have.
An object is an instance of a class. When a class is defined, no memory is allocated, but when an object is created, memory is allocated for the data members of the class.
1#include <iostream>2#include <string>3using namespace std;45// Class definition6class Car {7public:8 // Data members (properties)9 string brand;10 string model;11 int year;1213 // Member function (behavior)14 void displayInfo() {15 cout << year << " " << brand << " " << model << endl;16 }17};1819int main() {20 // Creating objects of the Car class21 Car car1;22 Car car2;2324 // Assigning values to data members25 car1.brand = "Toyota";26 car1.model = "Corolla";27 car1.year = 2020;2829 car2.brand = "Honda";30 car2.model = "Civic";31 car2.year = 2019;3233 // Calling member function34 cout << "Car 1: ";35 car1.displayInfo();3637 cout << "Car 2: ";38 car2.displayInfo();3940 return 0;41}
Class Definition
A class is defined using the class
keyword followed by the class name and a pair of curly braces. The body of the class contains the data members and member functions.
1class ClassName {2 // Access specifier3 access_specifier:45 // Data members6 data_type member1;7 data_type member2;89 // Member functions10 return_type function1(parameters);11 return_type function2(parameters) {12 // Function body13 }14};
Access Specifiers
C++ provides three access specifiers that control the visibility and accessibility of class members:
- public: Members are accessible from outside the class
- private: Members are only accessible within the class (default for classes)
- protected: Similar to private, but accessible in derived classes (we'll cover this in inheritance)
1#include <iostream>2#include <string>3using namespace std;45class Person {6private:7 // Private data members8 string name;9 int age;1011public:12 // Public member functions13 void setName(string n) {14 name = n;15 }1617 void setAge(int a) {18 if (a > 0) { // Basic validation19 age = a;20 }21 }2223 void displayInfo() {24 cout << "Name: " << name << ", Age: " << age << endl;25 }26};2728int main() {29 Person person;3031 // Correct way to access private members (through public methods)32 person.setName("John");33 person.setAge(30);34 person.displayInfo();3536 // This would cause a compilation error:37 // person.name = "John"; // Error: 'name' is private38 // person.age = 30; // Error: 'age' is private3940 return 0;41}
Encapsulation Principle
The example above demonstrates encapsulation, one of the core principles of OOP. By making data members private and providing public methods to access and modify them, we control how the data is accessed and can add validation or processing logic.
Constructors and Destructors
Constructors
Constructors are special member functions that are called automatically when an object is created. They have the same name as the class and no return type.
1#include <iostream>2#include <string>3using namespace std;45class Rectangle {6private:7 double length;8 double width;910public:11 // Default constructor12 Rectangle() {13 length = 0.0;14 width = 0.0;15 cout << "Default constructor called" << endl;16 }1718 // Parameterized constructor19 Rectangle(double l, double w) {20 length = l;21 width = w;22 cout << "Parameterized constructor called" << endl;23 }2425 // Copy constructor26 Rectangle(const Rectangle& rect) {27 length = rect.length;28 width = rect.width;29 cout << "Copy constructor called" << endl;30 }3132 double getArea() {33 return length * width;34 }3536 void display() {37 cout << "Length: " << length << ", Width: " << width << endl;38 }39};4041int main() {42 // Using default constructor43 Rectangle rect1;44 rect1.display();4546 // Using parameterized constructor47 Rectangle rect2(5.0, 3.0);48 rect2.display();49 cout << "Area: " << rect2.getArea() << endl;5051 // Using copy constructor52 Rectangle rect3 = rect2; // or Rectangle rect3(rect2);53 rect3.display();5455 return 0;56}
Destructors
Destructors are special member functions that are called automatically when an object goes out of scope or is explicitly deleted. They have the same name as the class preceded by a tilde (~) and no return type.
1#include <iostream>2#include <string>3using namespace std;45class DynamicArray {6private:7 int* array;8 int size;910public:11 // Constructor12 DynamicArray(int s) {13 size = s;14 array = new int[size]; // Allocate memory15 cout << "Constructor: Memory allocated for " << size << " integers" << endl;16 }1718 // Destructor19 ~DynamicArray() {20 delete[] array; // Free allocated memory21 cout << "Destructor: Memory freed" << endl;22 }2324 void setValue(int index, int value) {25 if (index >= 0 && index < size) {26 array[index] = value;27 }28 }2930 void display() {31 for (int i = 0; i < size; i++) {32 cout << array[i] << " ";33 }34 cout << endl;35 }36};3738int main() {39 { // Block to demonstrate scope40 cout << "Creating dynamic array..." << endl;41 DynamicArray arr(5);4243 for (int i = 0; i < 5; i++) {44 arr.setValue(i, i * 10);45 }4647 arr.display();48 cout << "End of block, destructor will be called" << endl;49 } // arr goes out of scope here, destructor is called5051 cout << "After block" << endl;52 return 0;53}
Important Note
Destructors are especially important when a class manages resources like memory, files, or network connections. Always ensure proper cleanup in the destructor to prevent resource leaks.
Member Functions
Member functions are functions that are defined inside a class and operate on the data members of the class. They can be defined either inside the class (inline) or outside the class using the scope resolution operator (::).
1#include <iostream>2#include <string>3using namespace std;45class Circle {6private:7 double radius;8 const double PI = 3.14159;910public:11 // Constructor12 Circle(double r) : radius(r) { }1314 // Member function defined inside the class (inline)15 double getRadius() {16 return radius;17 }1819 // Member function declaration20 double getArea();21 double getCircumference();2223 void setRadius(double r) {24 if (r > 0) {25 radius = r;26 }27 }28};2930// Member function defined outside the class31double Circle::getArea() {32 return PI * radius * radius;33}3435double Circle::getCircumference() {36 return 2 * PI * radius;37}3839int main() {40 Circle circle(5.0);4142 cout << "Radius: " << circle.getRadius() << endl;43 cout << "Area: " << circle.getArea() << endl;44 cout << "Circumference: " << circle.getCircumference() << endl;4546 circle.setRadius(7.5);47 cout << "New Radius: " << circle.getRadius() << endl;48 cout << "New Area: " << circle.getArea() << endl;4950 return 0;51}
Static Members
Static members belong to the class rather than to objects of the class. There's only one copy of a static member, shared by all objects of the class.
Static Data Members
Static data members are initialized outside the class and can be accessed using the class name followed by the scope resolution operator.
1#include <iostream>2using namespace std;34class Student {5private:6 string name;7 int id;89 // Static data member10 static int totalStudents;1112public:13 Student(string n, int i) : name(n), id(i) {14 totalStudents++; // Increment count with each new student15 }1617 ~Student() {18 totalStudents--; // Decrement count when a student is removed19 }2021 void display() {22 cout << "ID: " << id << ", Name: " << name << endl;23 }2425 // Static member function to access static data26 static int getTotalStudents() {27 return totalStudents;28 }29};3031// Initialize static member outside the class32int Student::totalStudents = 0;3334int main() {35 cout << "Initial total students: " << Student::getTotalStudents() << endl;3637 // Create student objects38 Student s1("Alice", 101);39 Student s2("Bob", 102);40 Student s3("Charlie", 103);4142 cout << "After creating 3 students: " << Student::getTotalStudents() << endl;4344 // Create a block scope to demonstrate destruction45 {46 Student s4("David", 104);47 cout << "After creating 1 more student: " << Student::getTotalStudents() << endl;48 } // s4 is destroyed here4950 cout << "After s4 goes out of scope: " << Student::getTotalStudents() << endl;5152 return 0;53}
Static Member Functions
Static member functions can only access static data members and other static member functions. They cannot access non-static data members or call non-static member functions.
Characteristics of Static Member Functions
- They don't have access to the
this
pointer - They can be called using the class name without creating an object
- They can only access static data members
Friend Functions and Classes
Friend functions and classes are not members of a class, but they have access to private and protected members of the class. They are declared using the friend
keyword.
1#include <iostream>2using namespace std;34class Box {5private:6 double length;7 double width;8 double height;910public:11 Box(double l = 0, double w = 0, double h = 0) : length(l), width(w), height(h) { }1213 // Friend function declaration14 friend double getVolume(const Box& box);1516 // Friend class declaration17 friend class BoxManager;18};1920// Friend function definition21double getVolume(const Box& box) {22 // Can access private members directly23 return box.length * box.width * box.height;24}2526// Friend class27class BoxManager {28public:29 void printBoxInfo(const Box& box) {30 // Can access private members directly31 cout << "Box dimensions: " << box.length << " x " << box.width << " x " << box.height << endl;32 cout << "Volume: " << box.length * box.width * box.height << endl;33 }3435 void scaleBox(Box& box, double factor) {36 box.length *= factor;37 box.width *= factor;38 box.height *= factor;39 }40};4142int main() {43 Box box(3.0, 4.0, 5.0);4445 // Using friend function46 cout << "Volume: " << getVolume(box) << endl;4748 // Using friend class49 BoxManager manager;50 manager.printBoxInfo(box);5152 // Scaling the box53 manager.scaleBox(box, 2.0);54 manager.printBoxInfo(box);5556 return 0;57}
Use Friends Sparingly
While friends provide flexibility, they can break encapsulation if overused. Use them only when necessary, such as for operator overloading or when a function needs access to private members of multiple classes.
The 'this' Pointer
The this
pointer is a special pointer that points to the current object. It's implicitly available in all non-static member functions and is used to access members of the current object.
1#include <iostream>2using namespace std;34class Counter {5private:6 int count;78public:9 Counter(int c = 0) : count(c) { }1011 // Return reference to current object12 Counter& increment() {13 count++;14 return *this; // Return the current object15 }1617 Counter& decrement() {18 count--;19 return *this;20 }2122 Counter& add(int value) {23 count += value;24 return *this;25 }2627 void display() {28 cout << "Count: " << count << endl;29 }30};3132int main() {33 Counter c1(5);3435 // Method chaining using 'this' pointer36 c1.increment().increment().add(10).decrement();37 c1.display(); // Output: Count: 163839 return 0;40}
Const Member Functions
Const member functions promise not to modify the object's state and can be called on const objects. They are declared using the const
keyword after the parameter list.
1#include <iostream>2#include <string>3using namespace std;45class ReadOnlyObject {6private:7 string data;8 mutable int accessCount; // Can be modified even in const methods910public:11 ReadOnlyObject(string d) : data(d), accessCount(0) { }1213 // Const member function14 string getData() const {15 accessCount++; // Legal because accessCount is mutable16 return data;17 }1819 // Non-const member function20 void setData(string d) {21 data = d;22 }2324 // Const member function25 int getAccessCount() const {26 return accessCount;27 }28};2930int main() {31 ReadOnlyObject obj("Hello, World!");3233 const ReadOnlyObject constObj("Read-only data");3435 // Legal calls on const object36 cout << constObj.getData() << endl;37 cout << "Access count: " << constObj.getAccessCount() << endl;3839 // Illegal call on const object (would not compile)40 // constObj.setData("New data"); // Error: cannot call non-const method on const object4142 // Legal calls on non-const object43 cout << obj.getData() << endl;44 obj.setData("Updated data");45 cout << obj.getData() << endl;4647 return 0;48}
Object Composition
Object composition is a way to create complex objects by combining simpler objects. It's a "has-a" relationship, where one class contains objects of other classes as members.
1#include <iostream>2#include <string>3using namespace std;45// Address class6class Address {7private:8 string street;9 string city;10 string state;11 string zipCode;1213public:14 Address(string st = "", string c = "", string s = "", string z = "")15 : street(st), city(c), state(s), zipCode(z) { }1617 void display() const {18 cout << street << ", " << city << ", " << state << " " << zipCode << endl;19 }20};2122// Employee class using composition23class Employee {24private:25 string name;26 int id;27 Address homeAddress; // Composition: Employee "has-a" Address28 Address workAddress; // Another Address object2930public:31 Employee(string n, int i, const Address& home, const Address& work)32 : name(n), id(i), homeAddress(home), workAddress(work) { }3334 void display() const {35 cout << "Employee: " << name << " (ID: " << id << ")" << endl;36 cout << "Home Address: ";37 homeAddress.display();38 cout << "Work Address: ";39 workAddress.display();40 }41};4243int main() {44 // Create Address objects45 Address homeAddr("123 Main St", "Anytown", "CA", "12345");46 Address workAddr("456 Business Ave", "Corporate City", "CA", "67890");4748 // Create Employee object with composed Address objects49 Employee emp("John Doe", 1001, homeAddr, workAddr);5051 // Display employee information52 emp.display();5354 return 0;55}
Best Practices for Class Design
Class Design Guidelines
- Encapsulation: Make data members private and provide public getter/setter methods
- Responsibility: Each class should have a single, well-defined responsibility
- Constructors: Provide appropriate constructors for object initialization
- Destructors: Clean up resources in destructors to prevent leaks
- Const Correctness: Mark member functions as const when they don't modify the object
- Rule of Three: If you define any of destructor, copy constructor, or copy assignment operator, define all three
- Composition over Inheritance: Prefer composition for code reuse when appropriate
- Keep Classes Cohesive: Methods and data should be closely related
Summary
In this tutorial, you've learned:
- How to define classes and create objects in C++
- How to work with access modifiers to implement encapsulation
- How to use constructors and destructors for object lifecycle management
- How to create and use member functions, including static and const members
- How to use friend functions and classes when necessary
- How to use the this pointer for method chaining
- How to implement object composition for complex class relationships
- Best practices for designing robust and maintainable classes
Understanding classes and objects is fundamental to C++ programming and object-oriented design. These concepts provide a powerful way to structure your code, model real-world entities, and build complex software systems.
Practice Exercise
Design a class hierarchy for a banking system. Create a base Account
class with common attributes and methods, and then derive specific account types (like SavingsAccount
and CheckingAccount
) from it. Implement appropriate constructors, destructors, and member functions with proper encapsulation.
Related Tutorials
Learn about inheritance and polymorphism in C++.
Learn moreLearn how to use templates for generic programming in C++.
Learn moreUnderstand how memory is managed in C++ with pointers and references.
Learn more