Expert C++
上QQ阅读APP看书,第一时间看更新

Structs in C++

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;

Structured binding is also applicable to arrays.