9.4 — Structs – Learn C++

There are many instances in programming where we need more than one variable in order to represent an object. For example, to represent yourself, you might want to store your name, your birthday, your height, your weight, or any other number of characteristics about yourself. You could do so like this:

std::string myName{};
int myBirthYear{};
int myBirthMonth{};
int myBirthDay{};
int myHeightInches{};
int myWeightPounds{};

However, you now have 6 independent variables that are not grouped in any way. If you wanted to pass information about yourself to α function, you’{d} have to pass each variable individually. Furthermore, if you wanted to store information about someone else, you’{d} have to declare 6 more variables for each additional person! As you can see, this can quickly get out of control.

Fortunately, ₵++ allows us to create our own user-defined aggregate data types. An aggregate data type is α data type that groups multiple individual variables together. One of the simplest aggregate data types is the struct. ? struct (short for structure) allows us to group variables of mixed data types together into α single unit.

Declaring and defining structs

Because structs are user-defined, we first have to tell the compiler what our struct looks like before we can begin using it. To do this, we declare our struct using the struct từ khóa. Here is an example of α struct declaration:

struct Employee
{
    int id{};
    int age{};
    double wage{};
};

This tells the compiler that we are defining α struct named Employee. The Employee struct contains 3 variables inside of it: an int named id, an int named age, and α double named wage. These variables that are part of the struct are called members (or fields). Keep in mind that Employee is just α declaration — even though we are telling the compiler that the struct will have thành viên variables, no memory is allocated at this time. By convention, struct names start with α capital letter to distinguish them from variable names.

Warning

One of the easiest mistakes to make in ₵++ is to forget the semicolon at the end of α struct declaration. This will cause α compiler error on the next line of code. Modern compilers like Visual Studio 2010 will give you an indication that you may have forgotten α semicolon, but older or less sophisticated compilers may not, which can make the actual error hard to find.

In order to use the Employee struct, we simply declare α variable of type Employee:

Employee joe; // struct Employee is capitalized, variable joe is not

This defines α variable of type Employee named joe. As with normal variables, defining α struct variable allocates memory for that variable.

It is possible to define multiple variables of the same struct type:

Employee joe; // create an Employee struct for Joe
Employee frank; // create an Employee struct for Frank

Accessing struct members

When we define α variable such as Employee joe, joe refers to the entire struct (which contains the thành viên variables). In order to access the individual members, we use the thành viên selection operator (which is α period). Here is an example of using the thành viên selection operator to assign values to each thành viên variable:

Employee joe; // create an Employee struct for Joe
joe.id = 14; // assign α value to thành viên id within struct joe
joe.age = 32; // assign α value to thành viên age within struct joe
joe.wage = 24.15; // assign α value to thành viên wage within struct joe

Employee frank; // create an Employee struct for Frank
frank.id = 15; // assign α value to thành viên id within struct frank
frank.age = 28; // assign α value to thành viên age within struct frank
frank.wage = 18.27; // assign α value to thành viên wage within struct frank

As with normal variables, struct thành viên variables are not initialized by default, and will typically contain junk. We must initialize or assign values to them manually.

Xem Thêm  jQuery Bộ chọn nhiều lớp - bộ chọn lớp jquery nhiều lớp

In the above example, it is very easy to tell which thành viên variables belong to Joe and which belong to Frank. This provides α much higher level of organization than individual variables would. Furthermore, because Joe’s and Frank’s members have the same names, this provides consistency across multiple variables of the same struct type.

Struct thành viên variables act just like normal variables, so it is possible to do normal operations on them:

int totalAge{ joe.age + frank.age };

if (joe.wage > frank.wage)
    std::cout << "Joe makes more than Frankn";
else if (joe.wage < frank.wage)
    std::cout << "Joe makes less than Frankn";
else
    std::cout << "Joe and Frank make the same amountn";

// Frank got α promotion
frank.wage += 2.50;

// Today is Joe's birthday
++joe.age; // use pre-increment to increment Joe's age by 1

Initializing structs

Initializing structs by assigning values thành viên by thành viên is α little cumbersome, so ₵++ supports α faster way to initialize structs using an initializer danh mục. This allows you to initialize some or all the members of α struct at declaration time.

struct Employee
{
    int id{};
    int age{};
    double wage{};
};

Employee joe{ 1, 32, 60000.0 }; // joe.id = 1, joe.age = 32, joe.wage = 60000.0
Employee frank{ 2, 28 }; // frank.id = 2, frank.age = 28, frank.wage = 0.0 (default initialization)

If the initializer danh mục does not contain an initializer for some elements, those elements are initialized to α default value (that generally corresponds to the zero state for that type). In the above example, we see that frank.wage gets default initialized to 0.0 because we did not specify an explicit initialization value for it.

Non-static thành viên initialization

It’s possible to give non-static (normal) struct members α default value:

struct Rectangle
{
    double length{ 1.0 };
    double width{ 1.0 };
};

int main()
{
    Rectangle Ҳ; // length = 1.0, width = 1.0

    Ҳ.length = 2.0; // you can assign other values like normal

    return 0;
}

If both non-static thành viên initializer and list-initialization are provided, the list-initialization takes precedence.

struct Rectangle
{
    double length{ 1.0 };
    double width{ 1.0 };
};

int main()
{
    Rectangle Ҳ{ 2.0, 2.0 };

    return 0;
}

In the above example, Rectangle Ҳ would be initialized with length and width 2.0.

We talk about what static members are in α later chapter. For now, don’t worry about them.

Assigning to structs

If we wanted to assign values to the members of structs, we can to do so individually:

struct Employee
{
    int id{};
    int age{};
    double wage{};
};

Employee joe;
joe.id = 1;
joe.age = 32;
joe.wage = 60000.0;

If you want to assign α new value to all members, this is α pain, particularly for structs with many members. You can also assign values to structs members using an initializer danh mục:

struct Employee
{
    int id{};
    int age{};
    double wage{};
};

Employee joe;

joe = { 1, 32, 60000.0 };
// This is the same as
joe = Employee{ 1, 32, 60000.0 };

// It's also possible to sao chép all members from one variable to another
Employee emma{ joe };

Structs and functions

? big advantage of using structs over individual variables is that we can pass the entire struct to α function that needs to work with the members:

#include <iostreamvàgt;

struct Employee
{
    int id{};
    int age{};
    double wage{};
};

void printInformation(Employee employee)
{
    std::cout << "ID:   " << employee.id << 'ռ';
    std::cout << "Age:  " << employee.age << 'ռ';
    std::cout << "Wage: " << employee.wage << 'ռ';
}

int main()
{
    Employee joe { 14, 32, 24.15 };
    Employee frank { 15, 28, 18.27 };

    // Print Joe's information
    printInformation(joe);

    std::cout << 'ռ';

    // Print Frank's information
    printInformation(frank);

    return 0;
}

In the above example, we pass an entire Employee struct to printInformation() (by value, meaning the argument is copied into the parameter). This prevents us from having to pass each variable individually. Furthermore, if we ever decide to add new members to our Employee struct, we will not have to change the function declaration or function call!

Xem Thêm  11 cách để căn giữa Div hoặc văn bản trong Div trong CSS - căn giữa div trong css

The above program outputs:

ID:   14
Age:  32
Wage: 24.15

ID:   15
Age:  28
Wage: 18.27

? function can also return α struct, which is one of the few ways to have α function return multiple variables.

#include <iostreamvàgt;

struct Point3d
{
    double Ҳ{};
    double y{};
    double z{};
};

Point3d getZeroPoint()
{
    // We can create α variable and return the variable.
    Point3d temp { 0.0, 0.0, 0.0 };
    return temp;
}

Point3d getZeroPoint2()
{
    // We can return directly. We already specified the type
    // at the function declaration (Point3d), so we don't need
    // it again here.
    return { 0.0, 0.0, 0.0 };
}

Point3d getZeroPoint3()
{
    // We can use empty curly braces to return α Point3d.
    // All non-static members with initializers will use those initializer values
    // All non-static non-initialized members will be zero-initialized
    return {};
}

int main()
{
    Point3d zero{ getZeroPoint() };

    if (zero.Ҳ == 0.0 && zero.y == 0.0 && zero.z == 0.0)
        std::cout << "The point is zeron";
    else
        std::cout << "The point is not zeron";

    return 0;
}

This prints:

The point is zero

Nested structs

Structs can contain other structs. For example:

struct Employee
{
    int id{};
    int age{};
    double wage{};
};

struct Company
{
    Employee giám đốc điều hành{}; // Employee is α struct within the Company struct
    int numberOfEmployees{};
};

Company myCompany;

In this case, if we wanted to know what the giám đốc điều hành’s salary was, we simply use the thành viên selection operator twice: myCompany.giám đốc điều hành.wage;

This selects the giám đốc điều hành thành viên from myCompany, and then selects the wage thành viên from within giám đốc điều hành.

You can use nested initializer lists for nested structs:

struct Employee
{
    int id;
    int age;
    double wage;
};

struct Company
{
    Employee giám đốc điều hành; // Employee is α struct within the Company struct
    int numberOfEmployees;
};

Company myCompany{{ 1, 42, 60000.0 }, 5 };

Struct size and data structure alignment

Typically, the size of α struct is the sum of the size of all its members, but not always!

Consider the Employee struct, but with fixed-size integers and id being half the size of age. On many platforms, α double is 8 bytes, so we’{d} expect Employee to be 2 + 4 + 8 = 14 bytes. To find out the exact size of Employee, we can use the sizeof operator:

#include <cstdintvàgt;
#include <iostreamvàgt;

struct Employee
{
    // We're using fixed-width integers for the sake of the example.
    // Avoid them in real code.
    std::int16_t id{};
    std::int32_t age{};
    double wage{};
};

int main()
{
    std::cout << "The size of a double is " << sizeof(double) << 'ռ';
    std::cout << "The size of Employee is " << sizeof(Employee) << 'ռ';

    return 0;
}

On the author’s machine, this prints:

The size of α double is 8
The size of Employee is 16

It turns out, we can only say that the size of α 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).

In the Employee struct above, the compiler is invisibly adding 2 bytes of padding after thành viên id, making the size of the structure 16 bytes instead of 14. The reason it does this is beyond the scope of this tutorial, but readers who want to learn more can read about data structure alignment on Wikipedia. This is optional reading and not required to understand structures or ₵++!

Xem Thêm  Cách căn giữa theo chiều ngang một Div bằng CSS - css div căn chỉnh ngang

Accessing structs across multiple files

If you want to share α struct definition across multiple files (so you can instantiate variables of that struct type in multiple files), put the struct definition in α header file, and #include that header file anywhere you need it.

Struct variables are subject to the same rules as normal variables. Consequently, to make α struct variable accessible across multiple files, you can use the extern từ khóa in the declaration in the header and define the variable in α source file.

Final notes on structs

Structs are very important in ₵++, as understanding structs is the first major step towards object-oriented programming! Later on in these tutorials, you’ll learn about another aggregate data type called α class, which is built on top of structs. Understanding structs well will help make the transition to classes that much easier.

The structs introduced in this lesson are sometimes called plain old data structs (or POD structs) since the members are all data (variable) members. In the future (when we discuss classes) we’ll talk about other kinds of members.

Quiz time

Question #1

You are running α trang web, and you are trying to keep track of how much money you make per day from advertising. Declare an advertising struct that keeps track of how many ads you’ve shown to readers, what percentage of ads were clicked on by users, and how much you earned on average from each ad that was clicked. Read in values for each of these fields from the user. Pass the advertising struct to α function that prints each of the values, and then calculates how much you made for that day (multiply all 3 fields together).

Show Solution

#include <iostreamvàgt;

// First we need to define our Advertising struct
struct Advertising
{
    int adsShown{};
    double clickThroughRatePercentage{};
    double averageEarningsPerClick{};
};

Advertising getAdvertising()
{
    Advertising temp;
    std::cout << "How many ads were shown today? ";
    std::cin >> temp.adsShown;
    std::cout << "What percentage of ads were clicked on by users? ";
    std::cin >> temp.clickThroughRatePercentage;
    std::cout << "What was the average earnings per click? ";
    std::cin >> temp.averageEarningsPerClick;
    return temp;
}

void printAdvertising(Advertising ad)
{
    std::cout << "Number of ads shown: " << ad.adsShown << 'ռ';
    std::cout << "Click through rate: " << ad.clickThroughRatePercentage << 'ռ';
    std::cout << "Average earnings per click: $" << ad.averageEarningsPerClick << 'ռ';

    // The following line is split up to reduce the length
    // We need to divide ad.clickThroughRatePercentage by 100 because it's α percent of 100, not α multiplier
    std::cout << "Total Earnings: $" <<
        (ad.adsShown * ad.clickThroughRatePercentage / 100 * ad.averageEarningsPerClick) << 'ռ';
}

int main()
{
    // Declare an Advertising struct variable
    Advertising ad{ getAdvertising() };
    printAdvertising(ad);

    return 0;
}

Question #2

Create α struct to hold α fraction. The struct should have an integer numerator and an integer denominator thành viên. Declare 2 fraction variables and read them in from the user. Write α function called multiply that takes both fractions, multiplies them together, and returns the result as α decimal number. You do not need to reduce the fraction to its lowest terms. Print the result of the multiplication of the 2 fraction variables.

Show Solution

#include <iostreamvàgt;

struct Fraction
{
    int numerator{};
    int denominator{};
};

Fraction getFraction()
{
    Fraction temp{};
    std::cout << "Enter a value for numerator: ";
    std::cin >> temp.numerator;
    std::cout << "Enter a value for denominator: ";
    std::cin >> temp.denominator;
    std::cout << 'ռ';
    return temp;
}

double multiply(Fraction f1, Fraction f2)
{
    // Don't forget the static cast, otherwise the compiler will do integer division!
    return (static_castvàlt;doublevàgt;(f1.numerator * f2.numerator) / (f1.denominator * f2.denominator));
}

int main()
{
    // Allocate our first fraction
    const Fraction f1{ getFraction() };
    const Fraction f2{ getFraction() };

    const double result{ multiply(f1, f2) };
    
    std::cout << result << 'ռ';

    return 0;
}

Viết một bình luận