Structs are almost the same as classes in C++. They have all the features of classes, and you can inherit a class from a structure and vice versa. The only difference between a class and a struct is the default visibility. For structs, the default visibility modifier is public. It relates to inheritance as well. For example, when you inherit a class from another class without using a modifier, it inherits privately. The following class inherits from Base privately:
class Base { public: void foo() {} };
class Derived : Base { // can access foo() while clients of Derived can't };
Following the same logic, the following struct inherits the Base publicly:
struct Base { // no need to specify the public section void foo() {} };
struct Derived : Base { // both Derived and clients of Derived can access foo() };
The same relates to the class that inherits from a struct. For example, the Derived class inherits from Base privately if not specified directly:
struct Base { void foo() {} };
// Derived inherits Base privately class Derived: Base { // clients of Derived can't access foo() };
In C++, structs and classes are interchangeable, but most programmers prefer to use structs for simple types. The C++ standard gives a better definition of simple types and calls them aggregates. A class (struct) is an aggregate if it conforms to the following rules:
No private or protected non-static data members
No user-declared or inherited constructors
No virtual, private, or protected base classes
No virtual member functions
Most of these rules will be a lot clearer after you finish this chapter. The following struct is an example of an aggregate:
struct Person { std::string name; int age; std::string profession; };
Before diving into inheritance and virtual functions, let's see what benefits aggregates bring when initializing. We can initialize Person objects in the following way:
Person john{"John Smith", 22, "programmer"};
C++20 provides even more fancy ways to initialize aggregates:
Person mary{.name = "Mary Moss", .age{22}, .profession{"writer"}};
Note how we mixed the initialization of members by designators.
Structured binding allows us to declare variables bound to aggregate members, as shown in the following code:
const auto [p_name, p_age, p_profession] = mary; std::cout << "Profession is: " << p_profession << std::endl;