Structs
in CS 102 on C++, Programming Basics
A data structure is several data elements grouped together under one name. These data elements are also known as members or variables of different types and sizes. Data structures, aka structs for short, can be declared in C++ using the following syntax:
struct StructName {
member_type1 member_name1;
member_type2 member_name2;
member_type2 member_name2;
...
};
An object can have a data type (as opposed to primitives which are int
or char
, etc.) of this structure. We can instantiate (aka create) an object by saying StructName name_of_object
.
This is useful, for example, when you want to represent a human being in your program, where you might want to store a string name
, a birthday (int birthDay
, int birthMonth
, and int birthYear
), and other characters about a person. You could do this:
struct Person {
string firstName;
string lastName;
int birthDay;
int birthMonth;
int birthYear;
...
};
You have grouped these variables, where you once (before you knew about structs) might have written something like this:
string firstName;
string lastName;
int birthDay;
int birthMonth;
int birthYear;
...
In this case, what happens if you wanted to store information about another person? You’d have to declare 5 more variables for each additional person. This can become too much to keep track off and your program will quickly get out of control.
Defining and Declaring Structs
struct Employee {
int id;
string firstName;
string lastName;
int age;
double hourlyWage;
string jobTitle;
string jobDescription;
};
The above struct declaration tells the compiler that we’re defining a new data structure called Employee
. An employee is characterized by 7 members/variables.
Note: One of the easiest mistakes when creating a struct
is forgetting the semicolon at the end of its declaration. Like declaring a primitive variable int num;
we end our declaration with a ;
Note: It’s by convention that we capitalize the structure’s name (e.g., Employee
not employee
). Why? In English class, we capitalize proper names, including any person, object, place, project, etc. Although, “object” may mean something else in English class than in Computer Science, the definitions aren’t really so different.
In order to use the Employee
struct, we simply declare a variable of type Employee
:
Employee profMe;
This defines a variable of type Employee
named profMe
. It is also possible to define multiple variables of the same struct (like we can declare many int
s in our program):
Employee profMe;
Employee detectiveSH;
Employee studentJS;
Accessing Struct Members
When we define a variable such as Employee profMe
, profMe
looks for its struct’s members. In other words, profMe
already has an int id
, string firstName
, int age
, … declared. We just need to access it (using the member selection operator– a period .
) and define those member variables.
Employee profMe;
profMe.id = 1003913;
profMe.firstName = "Rebecca";
profMe.lastName = "Ramnauth";
profMe.age = 37;
prof.hourlyWage = rand() % 100 + 11.50;
prof.jobTitle = "Professor of Computer Science";
prof.jobDescription = "Sleep. Eat. Code.";
Employee detectiveSH;
detectiveSH.firstName = "Sherlock";
detectiveSH.lastName = "Holmes";
detectiveSH.jobTitle = "Consulting Detective";
detectiveSH.jobDescription = "You know my methods, Watson.";
detectiveSH.id = 23987012;
detectiveSH.age = 43;
detectiveSH.hourlyWage = 0;
Initializer Lists
Let’s say you need to define 100 employees. Your program can get pretty long if you have to initialize each member at a time. Instead, C++ supports a faster way – using an initializer list. This allows you to initialize some or all of the struct members on the same line you declared an object of that struct exists.
struct Employee {
int id;
int age;
double wage;
};
int main() {
Employee john = {1, 45, 12.75};
Employee maggie = {2, 34, 30.25};
Employee alice = {3, 65, 0.00};
return 0;
}
Non-static Member Initialization
Starting with C++11, it’s possible to give non-static (aka normal) struct members a default value. But you cannot mix this initialization method with the initializer list’s syntax:
struct Rectangle
{
double length = 0.0;
double width = 0.0;
};
int main()
{
Rectangle x; // length = 0.0, width = 0.0
x.length = 2.0; // WORKS - you can assign other values like normal
Rectangle y = {5.5, 8.0}; // DOESN'T WORK
return 0;
}
However, in C++14, this restriction was lifted and both can be used.
Structs & Functions
A huge advantage with structs is that we can pass the entire struct to a function that needs to work with its members:
struct Employee {
int id;
string firstName;
string lastName;
int age;
double hourlyWage;
string jobTitle;
string jobDescription;
};
void printEmployeeInfo(Employee e);
int main() {
Employee me;
me.firstName = "Rebecca";
me.lastName = "Ramnauth";
me.jobTitle = "Professor of Computer Science";
me.jobDescription = "Sleep. Eat. Code.";
me.id = 10019823;
me.age = 37;
me.hourlyWage = rand() % 100 + 11.50;
printEmployeeInfo(me);
return 0;
}
void printEmployeeInfo(Employee e) {
cout << "Employee's ID: " << e.id << endl;
cout << "Employee's Name: " << e.firstName << " " << e.lastName << endl;
cout << "Employee's Age: " << e.age << endl;
cout << "Employee's Wage: " << "$" << e.hourlyWage << endl;
cout << "Employee's Annual Salary: " << "$" << e.hourlyWage * 40 * 52 << endl;
cout << endl;
}
In the above code, we pass an entire Employee struct to printEmployeeInfo
. Additionally, a function can return a struct, which is a great way to have a function return multiple variables.
Employee randomEmployee();
int main() {
Employee rand_guy = randomEmployee; // store returned Employee here.
cout << "Random person's id: " << rand_guy.id; // print its id
return 0;
}
Employee randomEmployee() {
Employee randomPerson;
randomPerson.id = rand() % 100000;
randomPerson.firstName = "John";
randomPerson.lastName = "Doe";
randomPerson.age = rand() % 100 + 18;
randomPerson.hourlyWage = rand() % 100 + 11.50;
randomPerson.jobTitle = "Some jobTitle here";
randomPerson.jobDescription = "Some jobDescription here";
}
Nested Structs
Structs can also contain other declared structs.
struct Job {
string jobTitle;
string jobDescription;
};
struct Employee {
int id;
string firstName;
string lastName;
int age;
double hourlyWage;
Job jobInfo;
};
If I wanted to know what someone’s job title is, I can simply use the member selection operator twice: myPerson.jobInfo.jobTitle;
Accessing Structs Across Multiple Files
Struct declarations don’t take up any memory. If you want to reuse a struct declaration across multiple files, put the struct declaration in a header file (extension is .h
), and #include
that header file anywhere you need it.
Note: When you use #include <file>
your compiler searches in the system directories and when you write #include "file"
it searches in the project (or current) directories.
Putting It Altogether
#include <iostream> #include <string>
using namespace std;
struct Employee {
int id;
string firstName;
string lastName;
int age;
double hourlyWage;
string jobTitle;
string jobDescription;
};
void printEmployeeInfo(Employee e);
int main() {
Employee me;
me.firstName = "Rebecca";
me.lastName = "Ramnauth";
me.jobTitle = "Professor of Computer Science";
me.jobDescription = "Sleep. Eat. Code.";
me.id = 10019823;
me.age = 38;
me.hourlyWage = rand() % 100 + 11.50;
Employee sh;
sh.firstName = "Sherlock";
sh.lastName = "Holmes";
sh.jobTitle = "Consulting Detective";
sh.jobDescription = "You know my methods, Watson.";
sh.id = 23987012;
sh.age = 43;
sh.hourlyWage = 0;
printEmployeeInfo(me);
printEmployeeInfo(sh);
return 0;
}
void printEmployeeInfo(Employee e) {
cout << "Employee's ID: " << e.id << endl;
cout << "Employee's Name: " << e.firstName << " " << e.lastName << endl;
cout << "Employee's Age: " << e.age << endl;
cout << "Employee's Wage: " << "$" << e.hourlyWage << endl;
cout << "Employee's Annual Salary: " << "$" << e.hourlyWage * 40 * 52 << endl;
cout << endl;
}