Skip to content

C++ 数据结构

虽然像数组这样的内置类型可以处理相同数据类型的集合,但 C++ 允许你使用 struct(结构体)和 class(类)定义自己的复合数据类型。它们允许你将可能不同类型的数据项组合到一个名称下。

结构体对于表示记录特别有用。想象一下管理一个数字图书馆;你会希望为每本书跟踪几个属性:

  • 标题 (string)
  • 作者 (string)
  • 主题 (string)
  • ISBN (string 或 数字)
  • 出版年份 (数字)

你使用 struct 关键字定义结构体。这会创建一个由其他类型(成员)组成的新的自定义数据类型。基本语法是:

struct StructureName {
DataType1 memberName1;
DataType2 memberName2;
// ... other members // ... 其他成员
};
// Optional: Variable declaration: StructureName variable1, variable2; // 可选:变量声明:StructureName variable1, variable2;

StructureName 成为程序中的一个新类型。每个成员定义都像一个标准的变量声明(例如,int year;,std::string title;)。你可以在定义后或稍后在代码中声明这种新类型的变量。以下是如何使用像 std::string 这样的现代 C++ 类型声明一个 Book 结构体:

#include <string> // Required for std::string // 使用 std::string 需要
#include <vector> // Often useful for collections of structs // 对于结构体集合通常有用
struct Book {
std::string title;
std::string author;
std::string subject;
std::string isbn; // Using string for ISBN is flexible // 对 ISBN 使用 string 很灵活
int publicationYear;
};
// Now you can declare variables of type Book: // 现在你可以声明 Book 类型的变量:
Book myFavoriteBook;

注意:在 C++ 中,与 C 不同,你不需要写 struct Book myFavoriteBook;。你可以直接使用结构体名称 Book 作为类型。

要访问结构体变量的成员,请使用成员访问运算符(.),它是一个放在变量名和成员名之间的点。

以下是一个示例,演示如何定义、声明、初始化和访问 Book 对象的成员:

#include <iostream>
#include <string>
#include <vector>
// Use the Book struct defined previously // 使用之前定义的 Book 结构体
struct Book {
std::string title;
std::string author;
std::string subject;
std::string isbn;
int publicationYear;
};
int main() {
// Declare and initialize Book objects using designated initializers (C++20)
// or aggregate initialization (earlier C++ versions)
// 使用指定初始化器(C++20)或聚合初始化(早期 C++ 版本)声明和初始化 Book 对象
Book book1 = {"The Hitchhiker's Guide", "Douglas Adams", "Science Fiction", "978-0345391803", 1979};
Book book2; // Declare another book // 声明另一本书
// Assign values using the member access operator (.) // 使用成员访问运算符 (.) 赋值
book2.title = "Effective Modern C++";
book2.author = "Scott Meyers";
book2.subject = "C++ Programming";
book2.isbn = "978-1491903995";
book2.publicationYear = 2014;
// Print Book1 info // 打印 Book1 信息
std::cout << "Book 1 Title: " << book1.title << std::endl;
std::cout << "Book 1 Author: " << book1.author << std::endl;
std::cout << "Book 1 Year: " << book1.publicationYear << std::endl;
std::cout << "--------------------" << std::endl;
// Print Book2 info // 打印 Book2 信息
std::cout << "Book 2 Title: " << book2.title << std::endl;
std::cout << "Book 2 Author: " << book2.author << std::endl;
std::cout << "Book 2 Year: " << book2.publicationYear << std::endl;
return 0;
}

编译并执行后,这段代码可能会产生:

Book 1 Title: The Hitchhiker's Guide
Book 1 Author: Douglas Adams
Book 1 Year: 1979
--------------------
Book 2 Title: Effective Modern C++
Book 2 Author: Scott Meyers
Book 2 Year: 2014

你可以像传递任何其他变量一样将结构体传递给函数。但是,按值传递大型结构体(会创建副本)效率可能较低。通常更好的做法是按引用(&)传递它们,如果函数不应该修改结构体,则按常量引用(const&)传递。

#include <iostream>
#include <string>
#include <vector>
// Use the Book struct defined previously // 使用之前定义的 Book 结构体
struct Book {
std::string title;
std::string author;
std::string subject;
std::string isbn;
int publicationYear;
};
// Function to print book details (passes by const reference to avoid copying) // 打印图书详细信息的函数(按常量引用传递以避免复制)
void printBook(const Book& book) { // Note: const Book& // 注意:const Book&
std::cout << "Title: " << book.title << std::endl;
std::cout << "Author: " << book.author << std::endl;
std::cout << "Subject: " << book.subject << std::endl;
std::cout << "ISBN: " << book.isbn << std::endl;
std::cout << "Year: " << book.publicationYear << std::endl;
std::cout << "--------------------" << std::endl;
}
int main() {
Book book1 = {"The Hitchhiker's Guide", "Douglas Adams", "Science Fiction", "978-0345391803", 1979};
Book book2 = {"Effective Modern C++", "Scott Meyers", "C++ Programming", "978-1491903995", 2014};
// Print info using the function // 使用函数打印信息
printBook(book1);
printBook(book2);
return 0;
}

通过 const& 传递表明函数不会改变 Book 对象,并且允许编译器通过避免完整的复制来进行潜在的优化。

你也可以使用指向结构体的指针,类似于指向其他类型的指针:

Book* bookPtr;

你可以使用取地址运算符(&)将结构体变量的地址赋给指针:

Book book1 = {/* ... initializers ... */ /* ... 初始化器 ... */};
bookPtr = &book1;

要通过指针访问结构体成员,请使用箭头运算符(->):

std::cout << "Title: " << bookPtr->title << std::endl;

以下是前一个示例,改写为在调用 printBook 时使用指针:

#include <iostream>
#include <string>
#include <vector>
// Use the Book struct defined previously // 使用之前定义的 Book 结构体
struct Book {
std::string title;
std::string author;
std::string subject;
std::string isbn;
int publicationYear;
};
// Function now accepts a pointer to a constant Book // 函数现在接受一个指向常量 Book 的指针
void printBookPtr(const Book* bookPtr) { // Note: const Book* // 注意:const Book*
if (bookPtr == nullptr) { // Always check pointers! // 总是检查指针!
std::cerr << "Error: Null pointer passed to printBookPtr.\n"; // 错误:空指针传递给 printBookPtr。
return;
}
std::cout << "Title: " << bookPtr->title << std::endl; // Use -> operator // 使用 -> 运算符
std::cout << "Author: " << bookPtr->author << std::endl;
std::cout << "Subject: " << bookPtr->subject << std::endl;
std::cout << "ISBN: " << bookPtr->isbn << std::endl;
std::cout << "Year: " << bookPtr->publicationYear << std::endl;
std::cout << "--------------------" << std::endl;
}
int main() {
Book book1 = {"The Hitchhiker's Guide", "Douglas Adams", "Science Fiction", "978-0345391803", 1979};
Book book2 = {"Effective Modern C++", "Scott Meyers", "C++ Programming", "978-1491903995", 2014};
// Print info by passing the address of each book // 通过传递每本书的地址来打印信息
printBookPtr(&book1);
printBookPtr(&book2);
return 0;
}

虽然指针是基础概念,但现代 C++ 通常倾向于使用引用或智能指针(std::unique_ptr、std::shared_ptr),而不是裸指针,以更安全地管理内存和对象生命周期。当仅仅访问对象数据时,通常首选前一节中所示的按引用(const&)传递,而不是按裸指针传递。

虽然 C++ 会自动使结构体名称(Book)可用作类型,但你可能想为复杂类型创建别名。现代 C++ 的做法是使用 using 关键字:

// Alias for a potentially complex type // 复杂类型的别名
using Bookshelf = std::vector<Book>;
// Alias for a pointer to a Book // Book 指针的别名
using BookPtr = Book*;
int main() {
Bookshelf myLibrary; // Use the alias 'Bookshelf' // 使用别名 'Bookshelf'
myLibrary.push_back({"Book Title", /*...*/});
Book book3 = {/*...*/ /*...*/};
BookPtr ptrToBook3 = &book3; // Use the alias 'BookPtr' // 使用别名 'BookPtr'
std::cout << ptrToBook3->title << std::endl;
return 0;
}

在现代 C++ 中,通常优先使用这种 using 语法而不是旧式的 C 风格 typedef 来创建类型别名,因为它更具一致性,尤其是在处理模板时。

在 C++ 中,struct 和 class 非常相似。主要的技术区别在于它们的默认成员可见性:struct 的成员默认是 public 的,而 class 的成员默认是 private 的。习惯上,struct 通常用于简单的聚合数据,旨在直接访问成员,而 class 则用于需要强制封装(隐藏数据并通过方法提供受控访问)的情况。你将在后面了解更多关于类和封装的内容。