Uploaded by ereindan

13.11 — Struct miscellany – Learn C++

advertisement
6/26/24, 11:26 AM
13.11 — Struct miscellany – Learn C++
LEARN C++
Skill up with our free tutorials
13.11 — Struct miscellany
 ALEX1
 JUNE 8, 2024
Structs with program-defined members
In C++, structs (and classes) can have members that are other program-defined types. There are
two ways to do this.
First, we can define one program-defined type (in the global scope) and then use it as a member of
another program-defined type:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
COPY
struct Employee
{
int id {};
int age {};
double wage {};
};
struct Company
{
int numberOfEmployees {};
Employee CEO {}; // Employee is a struct within the Company struct
};
int main()
{
Company myCompany{ 7, { 1, 32, 55000.0 } }; // Nested initialization list
to initialize Employee
std::cout << myCompany.CEO.wage << '\n'; // print the CEO's wage
return 0;
}
In the above case, we’ve defined an Employee struct, and then used that as a member in a
Company struct. When we initialize our Company , we can also initialize our Employee by using a
nested initialization list. And if we want to know what the CEO’s salary was, we simply use the
member selection operator twice: myCompany.CEO.wage;
Second, types can also be nested inside other types, so if an Employee only existed as part of a
Company, the Employee type could be nested inside the Company struct:
https://www.learncpp.com/cpp-tutorial/struct-miscellany/
1/13
6/26/24, 11:26 AM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
13.11 — Struct miscellany – Learn C++
#include <iostream>
struct Company
{
struct Employee // accessed via Company::Employee
{
int id{};
int age{};
double wage{};
};
int numberOfEmployees{};
Employee CEO{}; // Employee is a struct within the Company struct
};
int main()
{
Company myCompany{ 7, { 1, 32, 55000.0 } }; // Nested initialization list
to initialize Employee
std::cout << myCompany.CEO.wage << '\n'; // print the CEO's wage
return 0;
}
This is more often done with classes, so we’ll talk more about this in a future lesson (15.3 -- Nested
types (member types) (https://www.learncpp.com/cpp-tutorial/nested-types-member-types/)2).
Structs that are owners should have data members that are owners
In lesson 5.11 -- std::string_view (part 2) (https://www.learncpp.com/cpp-tutorial/stdstring_view-part-2/)3, we
introduced the dual concepts of owners and viewers. Owners manage their own data, and control
when it is destroyed. Viewers view someone else’s data, and do not control when it is altered or
destroyed.
In most cases, we want our structs (and classes) to be owners of the data they contain. This
provides a few useful benefits:
The data members will be valid for as long as the struct (or class) is.
The value of those data members won’t change unexpectedly.
The easiest way to make a struct (or class) an owner is to give each data member a type that is an
owner (e.g. not a viewer, pointer, or reference). If a struct or class has data members that are all
owners, then the struct or class itself is automatically an owner.
If a struct (or class) has a data member that is a viewer, it is possible that the object being viewed by
that member will be destroyed before the data member that is viewing it. If this happens, the struct
will be left with a dangling member, and accessing that member will lead to undefined behavior.
Best practice
https://www.learncpp.com/cpp-tutorial/struct-miscellany/
2/13
6/26/24, 11:26 AM
13.11 — Struct miscellany – Learn C++
In most cases, we want our structs (and classes) to be owners. The easiest way to enable this is
to ensure each data member has an owning type (e.g. not a viewer, pointer, or reference).
Author’s note
Practice safe structs. Don’t let your member dangle.
This is why string data members are almost always of type std::string (which is an owner), and
not opf type std::string_view (which is a viewer). The following example illustrates a case where
this matters:
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
#include <iostream>
#include <string>
#include <string_view>
struct Owner
{
std::string name{}; // std::string is an owner
};
struct Viewer
{
std::string_view name {}; // std::string_view is a viewer
};
// getName() returns the user-entered string as a temporary std::string
// This temporary std::string will be destroyed at the end of the full
expression
// containing the function call.
std::string getName()
{
std::cout << "Enter a name: ";
std::string name{};
std::cin >> name;
return name;
}
int main()
{
Owner o { getName() }; // The return value of getName() is destroyed just
after initialization
std::cout << "The owners name is " << o.name << '\n'; // ok
Viewer v { getName() }; // The return value of getName() is destroyed just
after initialization
std::cout << "The viewers name is " << v.name << '\n'; // undefined
behavior
return 0;
}
The getName() function returns the name the user entered as a temporary std::string . This
temporary return value is destroyed at the end of the full expression in which the function is called.
https://www.learncpp.com/cpp-tutorial/struct-miscellany/
3/13
6/26/24, 11:26 AM
13.11 — Struct miscellany – Learn C++
In the case of o , this temporary std::string is used to initialize o.name . Since o.name is a
std::string , o.name makes a copy of the temporary std::string . The temporary
std::string then dies, but o.name is not affected since it’s a copy. When we print o.name in the
subsequent statement, it works as we expect.
In the case of v , this temporary std::string is used to initialize v.name . Since v.name is a
std::string_view , v.name is just a view of the temporary std::string , not a copy. The
temporary std::string_view then dies, leaving v.name dangling. When we print v.name in the
subsequent statement, we get undefined behavior.
Struct size and data structure alignment
Typically, the size of a struct is the sum of the size of all its members, but not always!
Consider the following program:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
struct Foo
{
short a {};
int b {};
double c {};
};
int main()
{
std::cout << "The size of short is " << sizeof(short) << " bytes\n";
std::cout << "The size of int is " << sizeof(int) << " bytes\n";
std::cout << "The size of double is " << sizeof(double) << " bytes\n";
std::cout << "The size of Foo is " << sizeof(Foo) << " bytes\n";
return 0;
}
On the author’s machine, this printed:
1
2
3
4
The size of short is 2 bytes
The size of int is 4 bytes
The size of double is 8 bytes
The size of Foo is 16 bytes
Note that the size of short + int + double is 14 bytes, but the size of Foo is 16 bytes!
It turns out, we can only say that the size of a struct will be at least as large as the size of all the
variables it contains. But it could be larger! For performance reasons, the compiler will sometimes
add gaps into structures (this is called padding).
https://www.learncpp.com/cpp-tutorial/struct-miscellany/
4/13
6/26/24, 11:26 AM
13.11 — Struct miscellany – Learn C++
In the Foo struct above, the compiler is invisibly adding 2 bytes of padding after member a ,
making the size of the structure 16 bytes instead of 14.
For advanced readers
The reason compilers may add padding is beyond the scope of this tutorial, but readers who
want to learn more can read about data structure alignment
(https://en.wikipedia.org/wiki/Data_structure_alignment)
4
on Wikipedia. This is optional reading and not
required to understand structures or C++!
This can actually have a pretty significant impact on the size of the struct, as the following program
demonstrates:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
struct Foo1
{
short a{}; // will have 2 bytes of padding after a
int b{};
short c{}; // will have 2 bytes of padding after c
};
struct Foo2
{
int b{};
short a{};
short c{};
};
int main()
{
std::cout << sizeof(Foo1) << '\n'; // prints 12
std::cout << sizeof(Foo2) << '\n'; // prints 8
return 0;
}
This program prints:
12
8
Note that Foo1 and Foo2 have the same members, the only difference being the declaration
order. Yet Foo1 is 50% larger due to the added padding.
Tip
You can minimize padding by defining your members in decreasing order of size.
https://www.learncpp.com/cpp-tutorial/struct-miscellany/
5/13
6/26/24, 11:26 AM
13.11 — Struct miscellany – Learn C++
The C++ compiler is not allowed to reorder members, so this has to be done manually.

Next lesson

Back to table of contents

Previous lesson
13.12 Member selection with pointers and references
5
6
13.10 Passing and returning structs
7
8
B
U
URL
INLINE CODE
C++ CODE BLOCK
HELP!
Leave a comment...
 Name*
 Email*
Notify me about replies:


POST COMMENT
 Find a mistake? Leave a comment
above!
 Avatars from https://gravatar.com/11 are
connected to your provided email address.
19 COMMENTS
Newest 
Spesader
 May 18, 2024 12:57 pm
https://www.learncpp.com/cpp-tutorial/struct-miscellany/
6/13
6/26/24, 11:26 AM
13.11 — Struct miscellany – Learn C++
How did no one mention or thank Alex for his advice "Never let your member dangle"? Please pay
attention guys.
And thank you Alex, I'll be careful.
Reply
4
Rohit
 January 27, 2024 4:51 am
Can we say that we should arrange our data members in decreasing order of size so that padding
and hence size will be minimum?
 Last edited 4 months ago by Rohit
Reply
1
Alex
Author
 Reply to Rohit 12  January 28, 2024 6:28 pm
Yep, exactly that. I'll add to the lesson as a tip.
Reply
5
Karl
 November 29, 2023 12:22 am
Is padding something that we need to worry about? I'm suprised that the compiler isn't smart
enough to rearrange to order of the members to avoid padding anyway...
Reply
1
Alex
Author
 Reply to Karl 13  December 1, 2023 12:12 pm
It's not something you need to worry about unless you're allocating millions of objects.
4
Reply
Ali
 October 2, 2023 8:00 pm
Hello Alex,
https://www.learncpp.com/cpp-tutorial/struct-miscellany/
7/13
6/26/24, 11:26 AM
13.11 — Struct miscellany – Learn C++
In the example below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
struct Company
{
struct Employee // accessed via Company::Employee
{
int id{};
int age{};
double wage{};
};
int numberOfEmployees{};
Employee CEO{}; // Employee is a struct within the Company struct
};
int main()
{
Company myCompany{ 7, { 1, 32, 55000.0 } }; // Nested initialization list
to initialize Employee
std::cout << myCompany.CEO.wage << '\n'; // print the CEO's wage
return 0;
}
you've described Employee is accessed via Company::Employee, but for accessing CEO member
you've used the syntax myCompany.CEO.wage
Would you give an example using Company::Employee ?
Reply
0
Alex
Author
 Reply to Ali 14  October 3, 2023 9:11 pm
Company::Employee is the fully qualified name of the nested type. If we wanted to define an
Employee within main(), we'd use something like Company::Employee e; .
The dot syntax is for accessing the members of objects.
3
Reply
Yehor
 August 12, 2023 8:28 pm
https://www.learncpp.com/cpp-tutorial/struct-miscellany/
8/13
6/26/24, 11:26 AM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
13.11 — Struct miscellany – Learn C++
#include <iostream>
struct Company
{
struct Employee // accessed via Company::Employee
{
int id{};
int age{};
double wage{};
};
int numberOfEmployees{};
Employee CEO{}; // Employee is a struct within the Company struct
};
int main()
{
Company myCompany{ 7, { 1, 32, 55000.0 } }; // Nested initialization list
to initialize Employee
std::cout << myCompany.CEO.wage << '\n'; // print the CEO's wage
return 0;
}
When initializing myCompany with { 7, { 1, 32, 55000.0 } }, why is 7 the first argument if it is
supposed to be { 1, 32, 55000.0 }? As I see in the definition of struct Company, first is 'struct
Employee' and only then 'numberofEmployees'. Thank you!
Reply
0
Alex
Author
 Reply to Yehor 15  August 17, 2023 3:49 pm
struct Employee is just a type definition. It does not actually instantiate a variable.
There are two variables instantiated: numberOfEmployees (first), and CEO (second). We
initialize these variables in order.
4
Reply
Timo
 August 4, 2023 1:53 pm
https://www.learncpp.com/cpp-tutorial/struct-miscellany/
9/13
6/26/24, 11:26 AM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
13.11 — Struct miscellany – Learn C++
#include <iostream>
struct Foo1
{
short a{}; // will have 2 bytes of padding after a
int b{};
short c{}; // will have 2 bytes of padding after c
};
struct Foo2
{
int b{};
short a{};
short c{};
};
int main()
{
std::cout << sizeof(Foo1) << '\n'; // prints 12
std::cout << sizeof(Foo2) << '\n'; // prints 8
return 0;
}
So 32 bits are easier to process than 16 bits by a 64 bit CPU?
Also why isn't there any padding for Foo2 done by the compiler?
 Last edited 10 months ago by Timo
Reply
0
Alex
Author
 Reply to Timo 16  August 6, 2023 7:35 pm
Alignment boundaries are implementation defined (per compiler/architecture).
Members are generally aligned according to its size. A 4-byte int would be aligned on a 4-byte
boundary. A 2-byte short would be aligned on a 2-byte boundary. Padding at the end is used
so the whole struct is a size that is divisible by the member with the strictest alignment
requirements.
In Foo1, b must start on a 4-byte boundary. a is only 2 bytes in size, so we get 2 bytes of
padding. We also get 2 bytes of padding after c so the size of the struct is divisible by the
most strict alignment requirement (int, which has a 4 byte alignment requirement).
In Foo2, b must start on a 2-byte boundary. Since a is 4 bytes, and b will naturally fall on a
2-byte boundary. Same with c , it already falls on a 2-byte boundary. No end padding is
needed since the struct size is already divisible by the most strict alignment requirement.
6
Reply
https://www.learncpp.com/cpp-tutorial/struct-miscellany/
10/13
6/26/24, 11:26 AM
13.11 — Struct miscellany – Learn C++
Timo
 Reply to Alex 17  August 7, 2023 3:40 am
Ok thank you for the detailed explanation Alex!!
 Last edited 10 months ago by Timo
Reply
1
willwaywang6
 July 30, 2023 12:46 am
Excuse me, but I'm looking forward to shedding light on the data structure alignment a little
more.
Reply
1
Alex
Author
 Reply to willwaywang6 18  August 2, 2023 11:17 am
This tutorial series doesn't cover the topic further. Read the wikipedia article, and then you can
search out from there.
1
Reply
Daniel
 July 25, 2023 5:13 pm
Would it be possible to have a member be of type struct A in struct B and have a member be of
type struct B in struct A? Like the example below.
1
2
3
4
5
6
7
8
9
10
11
12
13
struct Company
{
int numberOfEmployees{};
Employee CEO{};
};
struct Employee
{
Company name{};
int id{};
int age{};
double wage{};
};
https://www.learncpp.com/cpp-tutorial/struct-miscellany/
11/13
6/26/24, 11:26 AM
13.11 — Struct miscellany – Learn C++
Reply
1
Alex
Author
 Reply to Daniel 19  July 28, 2023 6:49 pm
Not like this, as it would result in a circular dependency.
If you convert one or both of the members into a pointer (or better, a std::unique_ptr ),
then it is possible.
Reply
3
Daniel
 Reply to Alex 20  July 29, 2023 10:30 am
Thanks
1
Reply
Alok Ahn
 July 24, 2023 2:56 pm
No one must have read this chapter :/ Its quite nullptr out here
1
Reply
learnccp
lesson
learnccp lesson reviewer
reviewe  July 22, 2023 4:31 pm
First LOL
4
Reply
Links
1. https://www.learncpp.com/author/Alex/
https://www.learncpp.com/cpp-tutorial/struct-miscellany/
12/13
6/26/24, 11:26 AM
13.11 — Struct miscellany – Learn C++
2. https://www.learncpp.com/cpp-tutorial/nested-types-member-types/
3. https://www.learncpp.com/cpp-tutorial/stdstring_view-part-2/
4. https://en.wikipedia.org/wiki/Data_structure_alignment
5. https://www.learncpp.com/cpp-tutorial/member-selection-with-pointers-and-references/
6. https://www.learncpp.com/
7. https://www.learncpp.com/cpp-tutorial/passing-and-returning-structs/
8. https://www.learncpp.com/struct-miscellany/
9. https://www.learncpp.com/cpp-tutorial/constexpr-if-statements/
10. https://www.learncpp.com/cpp-tutorial/pass-by-const-lvalue-reference/
11. https://gravatar.com/
12. https://www.learncpp.com/cpp-tutorial/struct-miscellany/#comment-592947
13. https://www.learncpp.com/cpp-tutorial/struct-miscellany/#comment-590348
14. https://www.learncpp.com/cpp-tutorial/struct-miscellany/#comment-588141
15. https://www.learncpp.com/cpp-tutorial/struct-miscellany/#comment-585516
16. https://www.learncpp.com/cpp-tutorial/struct-miscellany/#comment-585092
17. https://www.learncpp.com/cpp-tutorial/struct-miscellany/#comment-585192
18. https://www.learncpp.com/cpp-tutorial/struct-miscellany/#comment-584781
19. https://www.learncpp.com/cpp-tutorial/struct-miscellany/#comment-584500
20. https://www.learncpp.com/cpp-tutorial/struct-miscellany/#comment-584697
https://www.learncpp.com/cpp-tutorial/struct-miscellany/
13/13
Download