C++ Lesson 02: Classes and Objects
2.1 struct in C++
In C++, the struct is expanded to contain data (variables) and functions. All the data and functions can be assigned to different access sections: private, protected, and public. It has the same capabilities as a class, except that. By default, a structure's members are public. The general form of a C++ structure is:
struct class_name : inheritance_list {
public:
// public members by default
protected:
// private members that can be inherited
privated:
// private members
} object_list;
The class_name is the type name of the structure, which is a class type. The individual members are referenced using the dot (.) operator when acting on an object or by using the arrow (->) operator when acting through a pointer to the object. The object_list and inheritance_list are optional.
Understanding the Similarities and Differences between Struct and Class in C++
Struct and class, two fundamental data structures in C++, often raise questions about their distinctions and similarities. This article provides a clear overview of the shared features and contrasting aspects of structs and classes, equipping you with the knowledge to make informed decisions when selecting the appropriate data structure for your programming needs.
Similarities
Both structs and classes are user-defined data types that allow you to group related variables and functions together. They both support the following features:
- Member variables: You can declare member variables of different data types within structs and classes.
- Member functions: You can define member functions within structs and classes to perform operations on the data members.
- Objects: You can create objects from both structs and classes to represent instances of the data structure.
- Constructors and Destructors: Both structs and classes can have constructors to initialize objects and destructors to clean up resources when objects are destroyed.
- Inheritance: Both structs and classes support inheritance, allowing you to create new data types that inherit properties and functionalities from existing ones.
- Access Specifiers: Both structs and classes support access specifiers (public, private, protected) to control the accessibility of member variables and functions.
Differences
Despite their similarities, structs and classes exhibit some key differences:
Feature | Struct | class |
Keyword for declaration | struct | class |
Default access specifier | public | private |
Purpose | Generally, the grouping of data | Data abstraction and further inheritance |
Usage | Generally used for small amounts of data | Generally used for large amounts of data |
Type | Value | Reference |
Storage | The object is created on the stack memory | The object is created on the heap memory |
Constructors and Destructors | Only parameterized constructors and destructors | All types of constructors and destructors |
In summary, structs are lightweight data structures primarily intended for grouping related data, while classes are more complex and versatile, enabling data abstraction, inheritance, and encapsulation. The choice between struct and class depends on the specific requirements of your application.
New struct in C++
#include <stdio.h>
#include <iostream>
using namespace std;
typedef struct STUDENTINFO{
private:
int age;
string name;
public:
void SetAge(int a);
void SetName(string n);
void PrintInfo();
} STUDENTINFO;
//------------------------------------------------------------------------------
int main()
{
STUDENTINFO Student;
string name;
int age;
age = 16;
name = "Ryan";
Student.SetAge(age);
Student.SetName(name);
Student.PrintInfo();
return 0;
}
//------------------------------------------------------------------------------
void STUDENTINFO::SetAge(int a)
{
age = a;
}
void STUDENTINFO::SetName(string n)
{
name = n;
}
void STUDENTINFO::PrintInfo()
{
cout << "Name: " << name << endl;
cout << "Age: " << age << endl;
}
//------------------------------------------------------------------------------
2.2 Classes and Objects
Classes
C++ creates a new user-defined data type — class. It is similar to the struct (structure), but some differences remain.
In C++, a class is a blueprint for creating objects. It defines the characteristics and behavior of a particular type of object. A class encapsulates data members (attributes) and member functions (methods) that define the properties and operations associated with that type of object.
Key characteristics of classes:
- User-defined data type: Classes are user-defined data types, allowing programmers to create new data structures tailored to specific requirements.
- Data encapsulation: Classes encapsulate data members, hiding them from direct access and controlling their modification through member functions.
- Data abstraction: Classes provide a layer of abstraction, hiding the implementation details of the object's data and behavior, exposing only the essential functionalities to the user.
- Code organization: Classes promote code organization by grouping related data and functions, enhancing maintainability and readability.
Components of a class:
- Data members: Data members represent the attributes of an object, such as its name, age, or salary. They are declared within the class using data types like int, string, or bool.
- Member functions: Member functions define the behavior of an object, specifying the actions it can perform. They are declared within the class using a return type, function name, and parentheses for parameters.
A class is a basic unit of encapsulation in C++. Its general form is as follows:
class class_name : inheritance_list {
// private members by default
private:
// private members
protected:
// private members that can be inherited
public:
// public members
} object_list;
A class in C++ is a user-defined data type that can hold its own data members and member functions in a block. The member data and functions in the class can be stored in different access sections: private, protected, and public. The default access section for the class is private. The class is similar to a struct, but the default access section is the public in a struct.
Example of a class
class Student {
public:
string name;
int age;
int rollNum;
void introduce() {
cout << "Hello! My name is " << name << ", and I am " << age << " years old." << endl;
}
};
In this example, the Student class defines the characteristics of a student object, including name, age, and rollNum. It also includes a member function, introduce(), which allows the student object to introduce itself.
Objects
An object is an instance of a class. It represents a concrete entity with the characteristics and behavior defined by the class. Objects are created using the class name and a pair of parentheses, often with initialization values.
Creating objects:
Student s1; // Creates an object named s1 of type Student
Student s2("Alice", 18, 12345); // Creates an object named s2 of type Student, initializing its data members
Accessing class members:
- Data members: Access data members using the dot (.) operator followed by the member name.
cout << s1.name << endl; // Outputs the name of s1
- Member functions: Call member functions using the dot (.) operator followed by the function name and parentheses for arguments.
s2.introduce(); // Calls the introduce() function of s2
Objects are the fundamental building blocks of object-oriented programming. They encapsulate data and behavior, enabling programmers to create reusable and modular components that interact with each other to solve complex problems.
In summary, classes and objects are essential concepts in C++ object-oriented programming. Classes provide blueprints for creating objects defining their data members and member functions. Objects, instances of classes, represent concrete entities with the characteristics and behavior defined by the class. Classes and objects enable programmers to model real-world entities and their interactions, creating well-structured and maintainable software solutions.
2.3 Constructors and Destructors
Constructors
In C++, a constructor is a special member function that is called automatically when an object of a class is created. It is used to initialize the object's data members with appropriate values. Constructors are essential to object-oriented programming, ensuring that objects are initialized consistently and well-defined.
Key characteristics of constructors:
- Same name as the class: Constructors have the same name as their class.
- No return type: Constructors do not have a return type, as they are responsible for initializing the object rather than returning a value.
- Parameter list: Constructors can have a parameter list for different ways of initializing objects.
- Access control: Constructors can have access specifiers (public, private, protected) to control their accessibility.
Types of constructors:
- Default constructor: A default constructor is a constructor that takes no arguments. It is used to initialize objects with default values.
- Parameterized constructor: A parameterized constructor is a constructor that takes one or more arguments. It allows for more flexibility in initializing objects with different values.
- Copy constructor: A copy constructor is a constructor that creates a new object by copying the values from an existing object. It is used to initialize objects from other objects of the same class.
Example of a default constructor
class Person {
public:
Person() {
name = "John Doe";
age = 30;
}
string name;
int age;
};
In this example, the Person class has a default constructor that initializes the name member variable to "John Doe" and the age member variable to 30.
Example of a parameterized constructor
class Student {
public:
Student(string name, int age, int rollNum) {
this->name = name;
this->age = age;
this->rollNum = rollNum;
}
string name;
int age;
int rollNum;
};
In this example, the Student class has a parameterized constructor that takes three arguments: name, age, and rollNum. It initializes the corresponding member variables with the values passed to the constructor.
Example of a copy constructor
class Employee {
public:
Employee(string name, string department) {
this->name = name;
this->department = department;
}
Employee(const Employee& other) {
name = other.name;
department = other.department;
}
string name;
string department;
};
In this example, the Employee class has a copy constructor that takes an Employee object as an argument. It initializes the new object's member variables with the existing object's values.
Constructors play a crucial role in object-oriented programming by ensuring that objects are properly initialized and ready for use. They provide a structured way to set up the initial state of objects and promote consistency and maintainability in code.
Destructors
In C++, a destructor is a special member function that is called automatically when an object of a class is destroyed. It is responsible for cleaning up any resources associated with the object, such as releasing allocated memory or closing open files. Destructors are essential to object-oriented programming, ensuring that objects are cleaned up properly, and resources are not leaked.
Key characteristics of destructors:
- Same name as the class: Destructors have the same name as the class they belong to, preceded by a tilde (~) symbol.
- No return type: Destructors do not have a return type, as they are responsible for cleaning up the object rather than returning a value.
- No parameter list: Destructors do not have a parameter list, as they are associated with a specific object and do not require additional input.
- Access control: Destructors can have access specifiers (public, private, protected) to control their accessibility.
Purpose of destructors:
- Release memory: Destructors are primarily responsible for releasing memory allocated for the object during its lifetime.
- Close resources: Destructors can also be used to close open files, handles, or other resources acquired by the object.
- Perform cleanup tasks: Destructors can perform necessary cleanup tasks, such as updating shared data structures or removing temporary files.
class File {
public:
File(string filename) {
filePointer = fopen(filename.c_str(), "r");
}
~File() {
if (filePointer) {
fclose(filePointer);
}
}
private:
FILE *filePointer;
};
In this example, the File class has a destructor that closes the file pointer opened in the constructor. This ensures that the file is properly closed when the File object is destroyed.
Importance of destructors:
Destructors are crucial for resource management and preventing memory leaks in C++. Destructors help maintain program efficiency and prevent system instability by ensuring that objects are cleaned up properly. They also promote code reusability and maintainability by encapsulating cleanup logic within the class.
In summary, destructors are essential components of object-oriented programming in C++. They are vital in resource management, memory deallocation, and overall program correctness. By providing a structured mechanism for object cleanup, destructors contribute to a more efficient and reliable programming environment.
2.4 C++ Access Specifiers
Access specifiers are keywords used in C++ to control the accessibility of class members (data members and member functions) to other classes and objects. They define the visibility of these members, determining which parts of a class can be accessed from outside the class itself.
Types of Access Specifiers
C++ provides three access specifiers:
Public
Public Access Specifier in C++
The public access specifier in C++ is used to declare class members (data members and member functions) that are accessible from anywhere in the program. Public members can be accessed from outside the class using the dot notation (.) with an object of the class.
Purpose of the Public Access Specifier
The primary purpose of the public access specifier is to provide the necessary interface for interacting with a class and its members. Public members represent the observable and controllable aspects of a class that users can directly interact with.
Key Characteristics of Public Members
- Accessibility: Public members are accessible from anywhere in the program, both within and outside the class itself.
- Visibility: Public members are visible to other classes and functions, allowing them to be accessed and used directly.
- Encapsulation: Public members still adhere to the principle of encapsulation, as they are still part of the class and not directly exposed to global scope.
When to Use the Public Access Specifier
The public access specifier should be used for:
- Data members that represent the state of an object and should be accessible for controlled modification.
- Member functions that provide operations or services related to the class's functionality and should be accessible to users.
- Constants and static data members represent global information related to the class and should be accessible for reference.
Examples of Public Access Specifiers 1
Examples of Public Access Specifiers
Consider the following example of a Student class:
class Student {
public:
string name;
int age;
void printDetails() {
cout << "Name: " << name << ", Age: " << age << endl;
}
};
In this example, the name, age, and printDetails() members are declared as public; this means that:
- The name and age data members can be accessed and modified directly from outside the Student class using an object of the class.
- The printDetails() member function can be called from outside the Student class using a class object to print the student's details.
Examples of Public Access Specifiers 2
Another example is a Rectangle class that inherits from the Shape class:
class Shape {
protected:
double width;
double height;
public:
void setDimensions(double w, double h) {
width = w;
height = h;
}
double getArea() {
return width * height;
}
};
class Rectangle : public Shape {
public:
double getPerimeter() {
return 2 * (width + height);
}
};
In this example, the setDimensions() and getArea() member functions of the Shape class are declared as public. This means that:
- An object of the Rectangle class can call the setDimensions() function of the Shape class to set its dimensions.
- An object of the Rectangle class can call the getArea() function of the Shape class to calculate its area.
The public access specifier plays a crucial role in defining the interface of a class and enabling controlled access to its members. By carefully using the public access specifier, programmers can achieve effective encapsulation, modularity, and maintainability in their C++ programs.
Private
Private Access Specifier in C++
The private access specifier in C++ is used to declare class members (data members and member functions) that are only accessible within the class itself. Private members cannot be accessed directly from outside the class, providing a higher level of encapsulation and protection for sensitive data.
Purpose of the Private Access Specifier
The primary purpose of the private access specifier is to enforce encapsulation, a fundamental principle of object-oriented programming. By restricting access to class members, private members prevent direct manipulation from outside the class, ensuring that data integrity and consistency are maintained.
Key Characteristics of Private Members
- Accessibility: Private members are only accessible from within the class itself, including member functions and nested classes.
- Visibility: Private members are hidden from outside the class, preventing direct access and modification from other classes or functions.
- Encapsulation: Private members strengthen encapsulation, ensuring that only authorized methods within the class can access and modify data, preserving data integrity and consistency.
When to Use the Private Access Specifier
The private access specifier should be used for:
- Data members represent the internal state of an object and should not be directly modified from outside the class.
- Implementation details that should be hidden from users and only accessed through controlled methods.
- Intermediate data structures or calculations that are part of the class's internal operations and should not be exposed to users.
Examples of Private Access Specifiers 1
Examples of Private Access Specifiers
Consider the following example of an Account class:
class Account {
private:
int balance;
public:
void deposit(int amount) {
balance += amount;
}
void withdraw(int amount) {
if (amount <= balance) {
balance -= amount;
} else {
cout << "Insufficient funds" << endl;
}
}
int getBalance() {
return balance;
}
};
In this example, the balance data member is declared as private. This means that:
- The balance member cannot be accessed directly from outside the Account class.
- The deposit(), withdraw(), and getBalance() member functions provide controlled access to the balance member, ensuring that modifications and retrievals are handled appropriately.
Examples of Private Access Specifiers 2
Another example is a Student class that manages student records:
class Student {
private:
string name;
int age;
double gpa;
public:
void setName(const string& newName) {
name = newName;
}
void setAge(int newAge) {
age = newAge;
}
void setGPA(double newGPA) {
gpa = newGPA;
}
string getName() const {
return name;
}
int getAge() const {
return age;
}
double getGPA() const {
return gpa;
}
};
This example declares the name, age, and gpa data members as private. This means that:
- An object of the Student class cannot directly modify the name, age, or gpa members.
- The setName(), setAge(), and setGPA() member functions provide controlled methods to modify these private members.
- The getName(), getAge(), and getGPA() member functions provide controlled methods to retrieve the values of these private members.
The private access specifier is an essential tool for achieving encapsulation and protecting sensitive data in C++ programs. By carefully using the private access specifier, programmers can ensure that data integrity and consistency are maintained, preventing unauthorized modifications and promoting a more controlled and secure environment for class members.
Protected
Protected Access Specifier in C++
The protected access specifier in C++ is a middle ground between the public and private access specifiers. It allows access to class members (data members and member functions) within the class itself and within derived classes (classes that inherit from the base class). However, it restricts access from outside the class hierarchy.
Purpose of the Protected Access Specifier
The primary purpose of the protected access specifier is to control access to class members that are intended for use within the class hierarchy but not directly accessible from outside the hierarchy. This promotes encapsulation while allowing derived classes to share and utilize certain aspects of the base class.
Key Characteristics of Protected Members
- Accessibility: Protected members are accessible from within the class itself and within derived classes.
- Visibility: Protected members are not visible to outside classes or functions, preventing direct access from outside the class hierarchy.
- Controlled Sharing: Protected members allow controlled sharing of functionality and data between a base class and its derived classes, promoting code reuse and inheritance patterns.
When to Use the Protected Access Specifier
The protected access specifier should be used for:
- Data members that represent shared state information relevant to both the base class and its derived classes.
- Member functions that provide common functionality or operations that are intended to be used within the class hierarchy.
- Implementation details or intermediate data structures that are part of the class hierarchy's internal operations but should not be exposed to outside users.
Examples of Protected Access Specifiers 1
Examples of Protected Access Specifiers
Consider the following example of a Shape class:
class Shape {
protected:
double width;
double height;
public:
void setDimensions(double w, double h) {
width = w;
height = h;
}
double getArea() {
return width * height;
}
};
class Rectangle : public Shape {
public:
double getPerimeter() {
return 2 * (width + height);
}
};
In this example, the width and height data members of the Shape class are declared as protected. This means that:
An object of the Shape class cannot directly access the width or height members.
An object of the Rectangle class, which inherits from Shape, can access the width and height members through inheritance.
The setDimensions() and getArea() member functions of the Shape class provide controlled access to the width and height members, allowing both the Shape class and its derived classes to manage and utilize this data.
Examples of Protected Access Specifiers 2
Another example is a Bank class that represents a bank account:
class Bank {
protected:
double balance;
int accountNumber;
public:
void deposit(int amount) {
balance += amount;
}
void withdraw(int amount) {
if (amount <= balance) {
balance -= amount;
} else {
cout << "Insufficient funds" << endl;
}
}
int getAccountNumber() const {
return accountNumber;
}
};
class SavingsAccount : public Bank {
public:
void applyInterest() {
balance *= 1.01;
}
};
In this example, the balance and accountNumber data members of the Bank class are declared as protected. This means that:
- An object of the Bank class cannot directly access the balance/ or accountNumber members.
- An object of the SavingsAccount class, which inherits from Bank, can access the balance and accountNumber members through inheritance.
- The deposit(), withdraw(), and getAccountNumber() member functions of the Bank class provide controlled access to the balance and accountNumber members, allowing both the Bank class and its derived classes to manage and utilize this data.
The protected access specifier plays a valuable role in inheritance hierarchies, enabling controlled sharing of data and functionality between a base class and its derived classes. By carefully using the protected access specifier, programmers can promote code reuse, maintain consistency within the class hierarchy, and ensure that sensitive data remains protected from unauthorized access.
Choosing the Right Access Specifier
The choice of access specifier depends on the desired level of encapsulation and control over class members. Public members are widely accessible, while private members provide the highest level of encapsulation. Protected members allow controlled access within the class hierarchy.
In general, it is recommended to use private members for data members that should not be directly modified from outside the class. Public members should be used for methods that provide controlled access to data members or perform specific operations. Protected members are useful when sharing data or functionality between a base class and its derived classes.
By carefully choosing the appropriate access specifiers, you can achieve better data protection, modularity, and maintainability in your C++ programs.
2.5 Class Member Access
Accessing Class Members in C++
In C++, class members (data members and member functions) are the building blocks of classes. They define the data and behavior of the class and determine how objects of that class can interact with the class and with each other. Accessing class members is a fundamental aspect of using C++ classes effectively.
Access Specifiers
C++ provides access specifiers that control the accessibility of class members. These access specifiers determine which parts of a class can be accessed from outside the class itself. The three main access specifiers are:
- Public: Public members are accessible from anywhere in the program, including outside the class itself.
- Private: Private members are only accessible within the class itself.
- Protected: Protected members are accessible within the class itself and within derived classes (classes that inherit from the base class).
Accessing Class Members Using Objects
Objects are instances of classes that represent real-world entities or concepts. To access class members using objects, the dot notation (.) is used. For example, if obj is an object of class MyClass, and member is a class member, then the following syntax is used to access the member:
obj.nember
For example, if MyClass has a public data member named name and a public member function named printDetails(), then the following code can be used to access these members:
MyClass obj;
obj.name = "John Doe";
obj.printDetails();
Accessing Class Members Using Static Members
Static members are class members that are not associated with any particular object of the class. They are accessible using the class name itself, not through an object. Static data members are accessed using the scope resolution operator (::), while static member functions are accessed using the class name followed by the scope resolution operator and the member function name.
For example, if MyClass has a static data member named count and a static member function named printCount(), then the following code can be used to access these members:
MyClass::count = 10;
MyClass::printCount();
Accessing Class Members from Derived Classes
Derived classes inherit members from their base classes. Inherited members can be accessed using the dot notation (.) with an object of the derived class. Protected members of the base class can also be accessed from derived classes using the dot notation.
For example, if MyClass has a protected data member named width and a protected member function named setWidth(), and Rectangle is a class derived from MyClass, then the following code can be used to access these members:
MyClass::count = 10;
MyClass::printCount();
Accessing Class Members from Derived Classes
Derived classes inherit members from their base classes. Inherited members can be accessed using the dot notation (.) with an object of the derived class. Protected members of the base class can also be accessed from derived classes using the dot notation.
For example, if MyClass has a protected data member named width and a protected member function named setWidth(), and Rectangle is a class derived from MyClass, then the following code can be used to access these members:
Rectangle rect;
rect.setWidth(10);
Accessing class members is essential for utilizing classes and objects effectively in C++. By understanding the different access specifiers and the syntax for accessing members using objects, static members, and derived classes, programmers can write well-structured, maintainable, and reusable C++ code.