C++ Structures (struct)
Introduction to Structures
In C++, a structure (struct) is a user-defined type that lets you group different kinds of variables under a single name.
Structures are useful for representing records with multiple attributes and are fundamental when modeling more complex data.
Basic Structure Syntax
struct StructureName {
dataType1 member1;
dataType2 member2;
// ... more members
};
Creating Structure Variables
You can declare structure variables in different ways:
// Method 1: Declare variables when defining the structure
struct Point {
int x;
int y;
} p1, p2;
// Method 2: Declare variables later
Point p3, p4;
Accessing Structure Members
Use the dot operator (.
) to access members of a struct:
#include <iostream>
#include <string>
struct Person {
std::string name;
int age;
float height;
};
int main() {
Person person1;
person1.name = "Alice";
person1.age = 30;
person1.height = 1.75f;
std::cout << "Name: " << person1.name;
}
Name: Alice
Initializing Structures
Structures can be initialized in several ways:
#include <string>
struct Person {
std::string name;
int age;
float height;
};
// Method 1: Aggregate initialization (brace-init)
Person alice = {"Alice", 30, 1.75f};
// Method 2: Designated initializers (C++20) — order must match member declarations
Person bob = {
.name = "Bob",
.age = 25,
.height = 1.80f
};
// Method 3: Assigning members individually
Person charlie; // members are default-initialized (std::string empty; ints/floats uninitialized until assigned)
charlie.name = "Charlie";
charlie.age = 35;
Structures vs Classes
Feature | struct | class |
---|---|---|
Default member access | public | private |
Default base-class inheritance access | public | private |
Usage (convention) | Often for plain data carriers | Often for encapsulation (data + behavior) |
Methods/ctors/dtors | Fully supported | Fully supported |
Memory model | Same as class | Same as struct |
Nested Structures
A structure can contain another structure as a member:
#include <string>
struct Address {
std::string street;
std::string city;
std::string zip;
};
struct Employee {
std::string name;
int id;
Address homeAddress;
};
Employee emp = {
"John Doe",
12345,
{"123 Main St", "Anytown", "12345"}
};
Arrays of Structures
You can create arrays of structures to manage groups of records:
#include <string>
constexpr int NUM_STUDENTS = 5;
struct Student {
std::string name;
float gpa;
};
Student classList[NUM_STUDENTS];
// Initialize array (remaining elements value-initialized)
Student students[NUM_STUDENTS] = {
{"Alice", 3.8f},
{"Bob", 3.5f},
{"Charlie", 3.2f}
};
Pointers to Structures
Access struct members via pointers using the arrow operator (->
):
#include <iostream>
#include <memory>
#include <string>
struct Person {
std::string name;
int age;
float height;
};
int main() {
Person person1{"Alice", 30, 1.75f};
Person* personPtr = &person1;
std::cout << personPtr->name << '\n'; // Access via pointer
// Prefer smart pointers over raw new/delete
auto newPerson = std::make_unique<Person>(Person{"Dave", 40, 1.85f});
std::cout << newPerson->name;
}
Structures with Functions
Structs can be passed to and returned from functions:
#include <iostream>
#include <string>
struct Person {
std::string name;
int age;
float height;
};
// Passing by value (makes a copy)
void printPerson(Person p) {
std::cout << "Name: " << p.name << '\n';
}
// Passing by reference (modifies caller's object)
void birthday(Person& p) {
++p.age;
}
// Returning a structure by value (RVO/NRVO makes this efficient)
Person createPerson(std::string name, int age) {
return Person{name, age, 1.70f};
}
Structure Methods
Structs can also define member functions:
#include <iostream>
struct Rectangle {
float width;
float height;
float area() const {
return width * height;
}
void resize(float w, float h) {
width = w;
height = h;
}
};
int main() {
Rectangle rect{5.0f, 3.0f};
std::cout << "Area: " << rect.area();
}
Area: 15
Constructors in Structures
Structures can have constructors just like classes:
#include <string>
struct Book {
std::string title;
std::string author;
int pages;
// Constructor (member-initializer list)
Book(std::string t, std::string a, int p)
: title(std::move(t)), author(std::move(a)), pages(p) {}
// Default constructor
Book() : title("Unknown"), author("Unknown"), pages(0) {}
};
Book novel("1984", "George Orwell", 328);
Real-World Example: Student Records
Here’s a complete example showing a struct with methods and an array of student records:
#include <iostream>
#include <iomanip>
#include <string>
struct Student {
std::string name;
int id;
float grades[3];
float average() const {
return (grades[0] + grades[1] + grades[2]) / 3.0f;
}
void display() const {
std::cout << std::left << std::setw(10) << name << " | "
<< std::right << std::setw(6) << id << " | "
<< std::fixed << std::setprecision(2)
<< grades[0] << ", " << grades[1] << ", " << grades[2]
<< " | " << average() << '\n';
}
};
int main() {
Student classList[3] = {
{"Alice", 1001, {85.5f, 90.0f, 88.5f}},
{"Bob", 1002, {78.0f, 82.5f, 80.0f}},
{"Charlie", 1003, {92.0f, 88.5f, 90.0f}}
};
std::cout << "Student Records:\n";
std::cout << "Name | ID | Grades | Average\n";
for (const Student& s : classList) {
s.display();
}
return 0;
}
Student Records: Name | ID | Grades | Average Alice | 1001 | 85.50, 90.00, 88.50 | 88.00 Bob | 1002 | 78.00, 82.50, 80.00 | 80.17 Charlie | 1003 | 92.00, 88.50, 90.00 | 90.17
Best Practices
- Prefer structs for simple data aggregates; use classes when you need encapsulation or invariants.
- Initialize all members (default member initializers or constructors help).
- Use member functions for operations specific to the struct’s data.
- Consider operator overloading when it makes usage clearer.
- Prefer smart pointers over raw ownership for dynamically allocated structs.
- When passing structs to functions: pass small/trivial types by value; for larger ones, prefer const reference unless you need to modify.