Structs#
Structs are a collection of data which represent a single entity.
Struct, short for “structure”, is a single variable with multiple fields. It can represent something more complex than a single integer or string, etc. Structs are composed from multiple fields, each with its own type.
You can think of a struct as a collection of other data types.
Syntax#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| // Syntax
struct NameOfStruct { // Name is capitalized by convention
type field1_name; // semicolon after each attribute
type field2_name;
// ... // Can have any number of attributes
type fieldN_name;
}; // semicolon at the end!
// Example
struct Pokemon {
int number;
string name;
string type;
string cry;
};
// Declare a Pokemon struct
// Create a Pokemon via Declaration, then Assignment
Pokemon bulbasaur;
bulbasaur.number = 1;
bulbasaur.name = "Bulbasaur";
bulbasaur.type = "Grass/Poison";
bulbasaur.cry = "Bulbaaa";
// Create a Pokemon via Initialization
Pokemon charmander = {4, "Charmander", "Fire", "char-char"};
// Access Operator (.)
Pokemon pikachu;
pikachu.name = "Pikachu"; // Assign to data member
cout << pikachu.name << endl; // Read data member
|
History#
structs come from the C programming language. structs in C++ can often be used with C code.
Terminology#
Data Members#
A struct is composed of one or more data members, each having a type of its own. Also known as attribute or property.
1
2
3
4
5
| // This struct has two data members
struct Student {
string name; // ← data members
double gpa; // ←
};
|
Access Operator#
The access operator is the dot .. It is used to access a data member of a struct, either for reading or assigning.
1
2
3
4
| Student s = {"Alice", 2.5};
cout << s.gpa;
// ↑ access operator
|
Aggregate Initialization#
structs can be initialized with an initializer list. This is called aggregate initialization
1
| Student s = {"Bob", 2.5}; // ← aggregate initialization
|
Conventions#
Struct Names Begin with a Capital Letter#
1
2
3
4
5
6
7
8
9
| struct Tree { // ✅ Follows naming convention
string species;
string genus;
};
struct tree { // ❌ Does not follow naming convention
string species;
string genus;
};
|
Structs are defined after #includes, and before functions#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| #include <iostream>
// ✅ Follows definition convention
struct Club {
string greek_name;
string latin_name;
};
void print_latin(Club c) {
cout << c.latin_name;
}
// ❌ Does not follows definition convention — below functions
struct Computer {
string os;
int memory;
double storage;
};
int main() {
Club dxd = {"ΔΧΔ", "Delta Chi Delta"};
print_latin(dxd);
return 0;
}
|
Usage#
Copy a struct#
structs can be copied via assignment. There are two techniques for copying structs:
- Copy the entire struct
- Copy one attribute of the struct
Copy the entire struct#
1
2
3
4
5
6
| Pokemon charmander = {4, "Charmander", "Fire", "char-char"};
// This assignment copies all attributes
Pokemon another_charmander = charmander;
cout << another_charmander.type << endl; // Prints "Fire"
|
Copy One Attribute#
1
2
3
4
5
6
7
8
9
10
11
12
| Pokemon bulbasaur = {1, "Bulbasaur", "Grass/Poison", "bulbaaa"};
// Declare another struct
Pokemon another_bulbasaur;
// One data member is copied with each assignment to the new struct
another_bulbasaur.number = bulbasaur.number;
another_bulbasaur.name = bulbasaur.name;
another_bulbasaur.type = bulbasaur.type;
another_bulbasaur.cry = bulbasaur.cry;
cout << another_bulbasaur.type << endl; // Prints "Grass/Poison"
|
Functions#
structs can be passed by value#
1
2
3
| void print_cry(Pokemon pokemon) {
cout << pokemon.cry << endl;
}
|
structs can be passed by reference, too#
1
2
3
4
5
6
7
8
9
10
| // Mutate the provided Pokemon struct based on its number
void evolve(Pokemon &pokemon) {
if (pokemon.number == 2) {
pokemon.number = 3;
pokemon.name = "Venusaur";
pokemon.type = "Grass/Poison";
pokemon.cry = "veeeena-saur";
}
// ...
}
|
Functions can return a struct#
1
2
3
4
5
| // Creates an Ivysaur with a nickname
Pokemon ivysaur_factory(string nickname) {
Pokemon ivysaur = {2, nickname, "Grass/Poison", "Iiiiivy-saur"};
return ivysaur;
}
|
Output#
structs cannot be output directly to cout.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| struct Tree {
string species;
string genus;
};
int main()
{
Tree red_oak = {"Quercus", "rubra"};
// ❌ Wrong — structs cannot be output directly
cout << red_oak << endl;
// ✅ Right — struct attributes can be output directly
cout << red_oak.genus << " " << red_oak.species << endl;
}
|
structs Can Contain Other structs#
The attributes of a struct can be any type, including other structs. This is called nested structs.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| struct PokemonType {
string primary;
string secondary;
};
struct Pokemon {
int number;
string name;
string cry;
PokemonType type;
};
int main() {
PokemonType grass_poison = {"Grass", "Poison"};
Pokemon bulbasaur = {1, "Bulbasaur", "Bulbaaa", grass_poison};
// Prints "Grass/Poison"
cout << bulbasaur.type.primary << "/" << bulbasaur.type.secondary << endl;
}
|
Structs & Vectors#
structs and vectors can be used together in two main ways:
- A vector of
structs - A struct with a
vector attribute
Vector of structs#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| struct Pokemon {
string name;
string cry;
};
int main() {
// Declare a Vector of structs
vector<Pokemon> trainer_team;
// Add existing Pokemon
Pokemon bulbasaur = {"Bulbasaur", "Bulbaaa"};
trainer_team.push_back(bulbasaur);
// Add new Pokemon from initializer list
trainer_team.push_back({"Caterpie", "cater-cater"});
trainer_team.push_back({"Rattata", "snarl"});
trainer_team.push_back({"Pidgey", "pidgeee"});
trainer_team.push_back({"Ekans", "sssss"});
trainer_team.push_back({"Nidoran♀", "chomp"});
// Prints "Go Caterpie! cater-cater"
cout << "Go "
<< trainer_team.at(1).name << "! "
<< trainer_team.at(1).cry << endl;
}
|
struct with Vector Attribute#
structs can have attributes which are themselves a vector of something.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
struct Pokemon {
string name;
string type;
vector<string> moves;
};
void print_pokemon(Pokemon &pokemon) {
cout << pokemon.name << " — " << pokemon.type << endl;
cout << "Moves:" << endl;
for (int i = 0; i < pokemon.moves.size(); ++i) {
cout << '\t' << i + 1 << ") " << pokemon.moves.at(i) << endl;
}
}
int main() {
Pokemon weedle = {"Weedle", "Bug/Poison"}; // Partially assign values
// Add values to the moves vector
weedle.moves.push_back("tackle");
weedle.moves.push_back("string shot");
weedle.moves.push_back("poison sting");
weedle.moves.push_back("bug bite");
// Or use Aggregate Initialization
Pokemon caterpie = {
"Caterpie", "Bug", {"tackle", "string shot", "bug bite"}};
// Print formatted Pokemon
print_pokemon(weedle);
cout << endl;
print_pokemon(caterpie);
}
|
Output
1
2
3
4
5
6
7
8
9
10
11
12
| Weedle — Bug/Poison
Moves:
1) tackle
2) string shot
3) poison sting
4) bug bite
Caterpie — Bug
Moves:
1) tackle
2) string shot
3) bug bite
|
Example: Point Struct#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| #include <iostream>
using namespace std;
struct Point {
int x;
int y;
};
// Translate Point by mutating the argument Point
void translate_x(Point &p, int magnitude) {
// Mutates the argument p (no return statement).
p.x += magnitude;
}
// Translate Point by returning a new Point
Point translate_y(const Point &p, int magnitude) {
// Returns a new Point made by copying the fields of p
return {p.x, p.y + magnitude};
}
int main() {
Point p1 = {1, 1};
// Modify & Output p1
cout << '(' << p1.x << ',' << p1.y << ')';
cout << " → ";
translate_x(p1, 1);
cout << '(' << p1.x << ',' << p1.y << ')' << endl;
// Modify & Output p2
Point p2 = {1, 1};
cout << '(' << p2.x << ',' << p2.y << ')';
cout << " → ";
// Returns a copy of p2, which we call p3
Point p3 = translate_y(p2, -5);
cout << '(' << p3.x << ',' << p3.y << ')' << endl;
}
|
Output
(1,1) → (2,1)
(1,1) → (1,-4)
Example Program: Pokémon Team#
- Create a team of Pokémon
- “Launch” each Pokémon in the team
- Evolve some Pokémon
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
| #include <iostream>
#include <string>
#include <vector>
using namespace std;
// Definition
struct Pokemon {
int number;
string name;
string type;
string cry;
};
// Functions
void launch(Pokemon &pokemon) {
cout << "Go "
<< pokemon.name << "! "
<< pokemon.cry << endl;
}
void evolve(Pokemon &pokemon) {
if (pokemon.number == 1) {
pokemon.number = 2;
pokemon.name = "Ivysaur";
pokemon.cry = "Iiiiivy-saur";
}
else if (pokemon.number == 2) {
pokemon.number = 3;
pokemon.name = "Venusaur";
pokemon.cry = "veeeena-saur";
}
// Add other evolutions ...
else {
cout << "No Evolution" << endl;
}
}
Pokemon ivysaur_factory(string name) {
Pokemon evolution = {2, name, "Grass/Poison", "Iiiiivy-saur"};
return evolution;
}
// Main
int main() {
vector<Pokemon> team;
Pokemon bulbasaur = {1, "Bulbasaur", "Grass/Poison", "Bulbaaa"};
Pokemon ivysaur = ivysaur_factory("Ralph");
Pokemon weedle = {13, "Weedle", "Bug/Poison", "dllllllll"};
team.push_back(bulbasaur);
team.push_back(ivysaur);
team.push_back(weedle);
for (auto p : team) {
launch(p);
}
evolve(bulbasaur); // Mutated its attributes
cout << bulbasaur.cry << endl; // Prints "Iiiiivy-saur"
}
|
Example Program: Interactive Pokémon Team#
- Initialize a team of Pokémon
- User selects a Pokémon in the team to “launch”
- “Launch” the selected Pokémon (output the Pokemon’s cry).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
| #include <iostream>
#include <string>
#include <vector>
using namespace std;
// Structs go below includes
struct Pokemon {
int number;
string name;
string type;
string cry;
};
// structs go above functions
// Print each Pokemon in the with a number prefix
void print_team(const vector<Pokemon> pokemon_team) {
for (int i = 0; i < pokemon_team.size(); i++) {
cout << i+1 << ". " << pokemon_team.at(i).name << endl;
}
}
// Returns Pokemon at 1-based index
Pokemon select_pokemon(const vector<Pokemon> pokemon_team, int num) {
Pokemon selected_pokemon = pokemon_team.at(num-1);
return selected_pokemon;
}
// Print the "Launch" output for the given Pokemon
void launch(Pokemon &p) {
cout << "GO! " << p.name << ". " << p.cry << endl;
}
// Create a Pokemon team. Launch a certain Pokemon from the team.
int main()
{
// 6 or less Pokemon
vector<Pokemon> team;
team.push_back({ 1,"bulbasaur", "grass/poison", "bulbaaaaaaaa" }); // 1
team.push_back({ 13, "Weedle", "Bug/Poison", "dllllllll" }); // 2
team.push_back({ 2, "Ivysaur", "Grass/Poison", "Iiiiivy-saur" }); // 3
team.push_back({ 11,"Caterpie", "Bug", "cater-cater" }); // 4
team.push_back({ 4,"charmander", "fire", "char-char" }); // 5
// Print Entire team with number prefix
print_team(team);
// Accept input (integer)
int user_selection;
cin >> user_selection;
Pokemon selected_pokemon = select_pokemon(team, user_selection);
// "Launch" Pokemon at from input (1-indexed)
launch(selected_pokemon); // "Launch" prints Pokemon's cry
return 0;
}
|