Progress6 of 20 topics

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.

cpp
1#include <iostream>
2#include <string>
3using namespace std;
4
5// Class definition
6class Car {
7public:
8 // Data members (properties)
9 string brand;
10 string model;
11 int year;
12
13 // Member function (behavior)
14 void displayInfo() {
15 cout << year << " " << brand << " " << model << endl;
16 }
17};
18
19int main() {
20 // Creating objects of the Car class
21 Car car1;
22 Car car2;
23
24 // Assigning values to data members
25 car1.brand = "Toyota";
26 car1.model = "Corolla";
27 car1.year = 2020;
28
29 car2.brand = "Honda";
30 car2.model = "Civic";
31 car2.year = 2019;
32
33 // Calling member function
34 cout << "Car 1: ";
35 car1.displayInfo();
36
37 cout << "Car 2: ";
38 car2.displayInfo();
39
40 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.

cpp
1class ClassName {
2 // Access specifier
3 access_specifier:
4
5 // Data members
6 data_type member1;
7 data_type member2;
8
9 // Member functions
10 return_type function1(parameters);
11 return_type function2(parameters) {
12 // Function body
13 }
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)
cpp
1#include <iostream>
2#include <string>
3using namespace std;
4
5class Person {
6private:
7 // Private data members
8 string name;
9 int age;
10
11public:
12 // Public member functions
13 void setName(string n) {
14 name = n;
15 }
16
17 void setAge(int a) {
18 if (a > 0) { // Basic validation
19 age = a;
20 }
21 }
22
23 void displayInfo() {
24 cout << "Name: " << name << ", Age: " << age << endl;
25 }
26};
27
28int main() {
29 Person person;
30
31 // Correct way to access private members (through public methods)
32 person.setName("John");
33 person.setAge(30);
34 person.displayInfo();
35
36 // This would cause a compilation error:
37 // person.name = "John"; // Error: 'name' is private
38 // person.age = 30; // Error: 'age' is private
39
40 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.

cpp
1#include <iostream>
2#include <string>
3using namespace std;
4
5class Rectangle {
6private:
7 double length;
8 double width;
9
10public:
11 // Default constructor
12 Rectangle() {
13 length = 0.0;
14 width = 0.0;
15 cout << "Default constructor called" << endl;
16 }
17
18 // Parameterized constructor
19 Rectangle(double l, double w) {
20 length = l;
21 width = w;
22 cout << "Parameterized constructor called" << endl;
23 }
24
25 // Copy constructor
26 Rectangle(const Rectangle& rect) {
27 length = rect.length;
28 width = rect.width;
29 cout << "Copy constructor called" << endl;
30 }
31
32 double getArea() {
33 return length * width;
34 }
35
36 void display() {
37 cout << "Length: " << length << ", Width: " << width << endl;
38 }
39};
40
41int main() {
42 // Using default constructor
43 Rectangle rect1;
44 rect1.display();
45
46 // Using parameterized constructor
47 Rectangle rect2(5.0, 3.0);
48 rect2.display();
49 cout << "Area: " << rect2.getArea() << endl;
50
51 // Using copy constructor
52 Rectangle rect3 = rect2; // or Rectangle rect3(rect2);
53 rect3.display();
54
55 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.

cpp
1#include <iostream>
2#include <string>
3using namespace std;
4
5class DynamicArray {
6private:
7 int* array;
8 int size;
9
10public:
11 // Constructor
12 DynamicArray(int s) {
13 size = s;
14 array = new int[size]; // Allocate memory
15 cout << "Constructor: Memory allocated for " << size << " integers" << endl;
16 }
17
18 // Destructor
19 ~DynamicArray() {
20 delete[] array; // Free allocated memory
21 cout << "Destructor: Memory freed" << endl;
22 }
23
24 void setValue(int index, int value) {
25 if (index >= 0 && index < size) {
26 array[index] = value;
27 }
28 }
29
30 void display() {
31 for (int i = 0; i < size; i++) {
32 cout << array[i] << " ";
33 }
34 cout << endl;
35 }
36};
37
38int main() {
39 { // Block to demonstrate scope
40 cout << "Creating dynamic array..." << endl;
41 DynamicArray arr(5);
42
43 for (int i = 0; i < 5; i++) {
44 arr.setValue(i, i * 10);
45 }
46
47 arr.display();
48 cout << "End of block, destructor will be called" << endl;
49 } // arr goes out of scope here, destructor is called
50
51 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 (::).

cpp
1#include <iostream>
2#include <string>
3using namespace std;
4
5class Circle {
6private:
7 double radius;
8 const double PI = 3.14159;
9
10public:
11 // Constructor
12 Circle(double r) : radius(r) { }
13
14 // Member function defined inside the class (inline)
15 double getRadius() {
16 return radius;
17 }
18
19 // Member function declaration
20 double getArea();
21 double getCircumference();
22
23 void setRadius(double r) {
24 if (r > 0) {
25 radius = r;
26 }
27 }
28};
29
30// Member function defined outside the class
31double Circle::getArea() {
32 return PI * radius * radius;
33}
34
35double Circle::getCircumference() {
36 return 2 * PI * radius;
37}
38
39int main() {
40 Circle circle(5.0);
41
42 cout << "Radius: " << circle.getRadius() << endl;
43 cout << "Area: " << circle.getArea() << endl;
44 cout << "Circumference: " << circle.getCircumference() << endl;
45
46 circle.setRadius(7.5);
47 cout << "New Radius: " << circle.getRadius() << endl;
48 cout << "New Area: " << circle.getArea() << endl;
49
50 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.

cpp
1#include <iostream>
2using namespace std;
3
4class Student {
5private:
6 string name;
7 int id;
8
9 // Static data member
10 static int totalStudents;
11
12public:
13 Student(string n, int i) : name(n), id(i) {
14 totalStudents++; // Increment count with each new student
15 }
16
17 ~Student() {
18 totalStudents--; // Decrement count when a student is removed
19 }
20
21 void display() {
22 cout << "ID: " << id << ", Name: " << name << endl;
23 }
24
25 // Static member function to access static data
26 static int getTotalStudents() {
27 return totalStudents;
28 }
29};
30
31// Initialize static member outside the class
32int Student::totalStudents = 0;
33
34int main() {
35 cout << "Initial total students: " << Student::getTotalStudents() << endl;
36
37 // Create student objects
38 Student s1("Alice", 101);
39 Student s2("Bob", 102);
40 Student s3("Charlie", 103);
41
42 cout << "After creating 3 students: " << Student::getTotalStudents() << endl;
43
44 // Create a block scope to demonstrate destruction
45 {
46 Student s4("David", 104);
47 cout << "After creating 1 more student: " << Student::getTotalStudents() << endl;
48 } // s4 is destroyed here
49
50 cout << "After s4 goes out of scope: " << Student::getTotalStudents() << endl;
51
52 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.

cpp
1#include <iostream>
2using namespace std;
3
4class Box {
5private:
6 double length;
7 double width;
8 double height;
9
10public:
11 Box(double l = 0, double w = 0, double h = 0) : length(l), width(w), height(h) { }
12
13 // Friend function declaration
14 friend double getVolume(const Box& box);
15
16 // Friend class declaration
17 friend class BoxManager;
18};
19
20// Friend function definition
21double getVolume(const Box& box) {
22 // Can access private members directly
23 return box.length * box.width * box.height;
24}
25
26// Friend class
27class BoxManager {
28public:
29 void printBoxInfo(const Box& box) {
30 // Can access private members directly
31 cout << "Box dimensions: " << box.length << " x " << box.width << " x " << box.height << endl;
32 cout << "Volume: " << box.length * box.width * box.height << endl;
33 }
34
35 void scaleBox(Box& box, double factor) {
36 box.length *= factor;
37 box.width *= factor;
38 box.height *= factor;
39 }
40};
41
42int main() {
43 Box box(3.0, 4.0, 5.0);
44
45 // Using friend function
46 cout << "Volume: " << getVolume(box) << endl;
47
48 // Using friend class
49 BoxManager manager;
50 manager.printBoxInfo(box);
51
52 // Scaling the box
53 manager.scaleBox(box, 2.0);
54 manager.printBoxInfo(box);
55
56 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.

cpp
1#include <iostream>
2using namespace std;
3
4class Counter {
5private:
6 int count;
7
8public:
9 Counter(int c = 0) : count(c) { }
10
11 // Return reference to current object
12 Counter& increment() {
13 count++;
14 return *this; // Return the current object
15 }
16
17 Counter& decrement() {
18 count--;
19 return *this;
20 }
21
22 Counter& add(int value) {
23 count += value;
24 return *this;
25 }
26
27 void display() {
28 cout << "Count: " << count << endl;
29 }
30};
31
32int main() {
33 Counter c1(5);
34
35 // Method chaining using 'this' pointer
36 c1.increment().increment().add(10).decrement();
37 c1.display(); // Output: Count: 16
38
39 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.

cpp
1#include <iostream>
2#include <string>
3using namespace std;
4
5class ReadOnlyObject {
6private:
7 string data;
8 mutable int accessCount; // Can be modified even in const methods
9
10public:
11 ReadOnlyObject(string d) : data(d), accessCount(0) { }
12
13 // Const member function
14 string getData() const {
15 accessCount++; // Legal because accessCount is mutable
16 return data;
17 }
18
19 // Non-const member function
20 void setData(string d) {
21 data = d;
22 }
23
24 // Const member function
25 int getAccessCount() const {
26 return accessCount;
27 }
28};
29
30int main() {
31 ReadOnlyObject obj("Hello, World!");
32
33 const ReadOnlyObject constObj("Read-only data");
34
35 // Legal calls on const object
36 cout << constObj.getData() << endl;
37 cout << "Access count: " << constObj.getAccessCount() << endl;
38
39 // Illegal call on const object (would not compile)
40 // constObj.setData("New data"); // Error: cannot call non-const method on const object
41
42 // Legal calls on non-const object
43 cout << obj.getData() << endl;
44 obj.setData("Updated data");
45 cout << obj.getData() << endl;
46
47 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.

cpp
1#include <iostream>
2#include <string>
3using namespace std;
4
5// Address class
6class Address {
7private:
8 string street;
9 string city;
10 string state;
11 string zipCode;
12
13public:
14 Address(string st = "", string c = "", string s = "", string z = "")
15 : street(st), city(c), state(s), zipCode(z) { }
16
17 void display() const {
18 cout << street << ", " << city << ", " << state << " " << zipCode << endl;
19 }
20};
21
22// Employee class using composition
23class Employee {
24private:
25 string name;
26 int id;
27 Address homeAddress; // Composition: Employee "has-a" Address
28 Address workAddress; // Another Address object
29
30public:
31 Employee(string n, int i, const Address& home, const Address& work)
32 : name(n), id(i), homeAddress(home), workAddress(work) { }
33
34 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};
42
43int main() {
44 // Create Address objects
45 Address homeAddr("123 Main St", "Anytown", "CA", "12345");
46 Address workAddr("456 Business Ave", "Corporate City", "CA", "67890");
47
48 // Create Employee object with composed Address objects
49 Employee emp("John Doe", 1001, homeAddr, workAddr);
50
51 // Display employee information
52 emp.display();
53
54 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 SavingsAccountand CheckingAccount) from it. Implement appropriate constructors, destructors, and member functions with proper encapsulation.

Related Tutorials

Learn about inheritance and polymorphism in C++.

Learn more

Learn how to use templates for generic programming in C++.

Learn more

Understand how memory is managed in C++ with pointers and references.

Learn more