Structs and Classes
Contents
2.1. Structs and Classes#
In C/C++, we can group together multiple data members of different data types in a data structure. For example, consider the following student
structure:
Code
#include <iostream> using namespace std;
struct Student { string name; int ID; };
int main(void) { struct Student x, y; x.name = "Kenya"; x.ID = 8012; y.name = "Cindy"; x.ID = 2023; cout << "X, Name: " << x.name << endl << "ID: " << x.ID << endl; return 0; }
In this structure, we have grouped together a string and an integer. The struct definition does not involve any memory allocation. When we create an instance of the struct Student
, only then a memory space is reserved for that instance. Multiple instances of the student structure x
and y
were created, and each one stores different values.
To access data members of a structure, we use the dot operator (.
) on the instance. For example, to access the name
data member of the x
instance, we write x.name
.
Problem! However in line 15, when we wanted to print the data members of Student x
, we had to access the name
and ID
to print them. Unfortunately, there cannot be functions, like a print function, associated with struct student
to help us do a printing operation on Student x
without accessing each data member. If there were many data members, the printing process would be challenging.
2.1.1. What is a class?#
A class is an expansion to structures in C/C++. It allows us to group data members and functions in a single unit.
A class is a user-defined data type. Creating an instance of a class is called instantiation and the instance is called an object. Only object have memory allocated for them, but the class definition does not. You can think of a class as a template/blueprint for creating objects.
2.1.2. Defining a class#
To define a class, we use the class
keyword followed by the class name. To divide code into multiple file, we will have the class definition in a header file, then function implementations for that class in a source file. For example, the following code defines a class Student
with two data members name
and ID
. We also define a function print
that prints the data members of the class.
Code in Student.h
#include <string>
using namespace std;
class Student {
string name;
int ID;
void print();
};
2.1.3. Access Control#
Members in a class are private by default. This means that the name
, ID
and print
cannot be used outside the class, for example, in the main function.
To control access to the members of a class, we can use access specifiers/modifiers. The common two are private
and public
. These access modifiers allow us to dictate which members are:
private
: can only be used within a class, andpublic
: can be used inside and outside the class.
For example, we will re-write the class definition in Student.h
to have name
and ID
as private members and print
as a public member. name
and ID
can only be accessed within the class, while print
can be accessed outside the class.
Code in Student.h
#include <string>
using namespace std;
class Student {
private:
string name;
int ID;
public:
void print();
};
To understand further how access specifiers work, we need to create instances of the class and access the members. However, we cannot yet create instances of the class because we have not implemented the function members of Student
.
2.1.4. Implementing functions in a class#
In a source file named Student.cpp
, we will implement the function member of the class Student
. The function print
will print the data members of the class.
Code in Student.cpp
1#include <iostream>
2#include "Student.h"
3using namespace std;
4
5void Student::print() {
6 cout << "Name: " << name << endl << "ID: " << ID << endl;
7}
Lines 1 and 3. We include the iostream
library and write using namespace std;
to use cout
in print
.
Line 2. We include the header file Student.h
to access the class Student
.
Line 5. We define the function print
that prints the data members of the class. print
is being implemented outside of the class definition, which is in Student.h
. Hence, we need to specify in Student.cpp
in the function header that print
is part of Student
class. To do so, we append to the function name the class name Student
and use the scope resolution operator ::
, i.e., Student::print
.
2.1.5. Creating instances of a class#
We can then create instances of the class Student
in the main
function in main.cpp
Output is undefined
Name: ? ID: ?
Line 6. We create an instance of the class Student
named x
. This requires only the name of the class followed by the name of the object. In the memory, a memory space for object x
is created. That space stores two data members: name
and ID
. Since name
and ID
are uninitialized, they will have garbage values denoted by ?
.
Line 7 [Erroneous]. We incorrectly attempt to access the data member name
of the object x
using the dot operator. However, we cannot access it because it is a private member of Student
class.
Line 8. We call the print
function on the instance x
. The output will be undefined because the data members name
and ID
are uninitialized.
2.1.6. Getter and Setter functions#
To help gain access to the private members of name
and ID
, we can create getter and setter functions. Getter functions are used to access/return the value of a private member, while setter functions are used to set the value of a private member.
We will create getter and setter functions for only the name
data member of the Student
class.
Code in Student.h
1#include <string>
2using namespace std;
3
4class Student {
5 private:
6 string name;
7 int ID;
8
9 public:
10 void print();
11 void setName(string name);
12 string getName();
13};
In Student.h
file, in lines 11 and 12, we add the function declarations for getName
and setName
functions to the Student
class.
getName
: The getter forname
will return the value ofname
and received nothing as an input.setName
: The setter forname
will receive a string as an input and assign it toname
.
Code in Student.cpp
1#include <iostream>
2#include "Student.h"
3using namespace std;
4
5void Student::print() {
6 cout << "Name: " << name << endl << "ID: " << ID << endl;
7}
8
9void Student::setName(string n) {
10 name = n;
11}
12
13string Student::getName() {
14 return name;
15}
In Student.cpp
file, in lines 14 to 20, we add the function implementations for getName
and setName
functions.
Code in main.cpp
1#include <iostream>
2#include "Student.h"
3using namespace std;
4
5int main(void) {
6 Student x;
7
8 x.setName("Cindy");
9 cout << x.getName() << endl;
10
11 x.print();
12 return 0;
13}
Output (ID is undefined)
Cindy Name: Cindy ID: ?
In main.cpp
file, in line 8, we invoke the setName
function on object x
, and pass "Cindy"
to the function. This calls the setName()
which is capable of accessing name
member of x
and set it to "Cindy"
.
Therefore, in line 9, when we invoke the getName
function on object x
, we return the value of name
stored in object x
. Then, we print it to the user as "Cindy"
.
In line 11, we are still invoking print()
on x
while it has an uninitialized ID
. The output will be Name: Cindy
and ID: <undefined>
.
getter cannot be used to modify
Getters like getName
return the value of a private member, but they cannot be used to modify the value of the private member. For example, we cannot write x.getName() = "Cindy"
to change the value of name
in object x
. This is because getName
returns a copy of the value of name
, which cannot be assigned to another value. getName
does not return a reference to name
.
2.1.7. Summary#
In this section, we have learned about classes in C++. We have seen how to
define a class,
create instances of a class,
access the members of a class,
control access to the members of a class using access specifiers/modifiers, i.e.
private
andpublic
,implement functions in a class,
create getter and setter functions to access private members of a class.
In the next section, we will learn about how can we set private members of a class using constructors at the time of instantiation without need for setter functions.