文章目录
基础演示案例1
当你在编写模块化的 C++ 代码时,目标是创建可重用、易于维护并且功能明确的组件。下面是一个简单的例子,展示如何将一个程序分解成几个模块。我们将创建一个简单的数学运算库,包括加法和乘法函数,并且使用一个主函数来演示这些功能。
首先,我们从定义一个头文件(header file)开始,这个头文件会包含我们的函数声明。
math_functions.h
#ifndef MATH_FUNCTIONS_H
#define MATH_FUNCTIONS_H
// 函数声明
double add(double x, double y);
double multiply(double x, double y);
#endif // MATH_FUNCTIONS_H
然后,我们需要创建一个源文件(source file),在这个文件中定义我们的函数。
math_functions.cpp
#include "math_functions.h"
// 函数定义
double add(double x, double y) {
return x + y;
}
double multiply(double x, double y) {
return x * y;
}
最后,我们需要一个主程序来测试这些函数。
main.cpp
#include <iostream>
#include "math_functions.h"
int main() {
double result_add = add(5.0, 3.0);
double result_multiply = multiply(5.0, 3.0);
std::cout << "Addition: " << result_add << std::endl;
std::cout << "Multiplication: " << result_multiply << std::endl;
return 0;
}
为了编译这些文件,你可以使用命令行工具,假设你将所有的文件放在同一个目录下,并且使用 g++ 编译器:
g++ -c math_functions.cpp
g++ -o my_program main.cpp math_functions.o
第一条命令将 math_functions.cpp
编译为对象文件 math_functions.o
。第二条命令则链接 main.cpp
和 math_functions.o
,并生成一个名为 my_program
的可执行文件。
以上就是构建一个简单模块化 C++ 应用程序的基本步骤。在实际开发中,你可能还需要考虑其他因素如错误处理、单元测试等。此外,对于较大的项目,可以使用构建系统(如 CMake 或 Makefiles)来自动化编译过程。
案例扩展
让我们继续扩展这个模块化的 C++ 代码示例,以包含更多的功能和结构上的改进。
扩展数学库
我们可以为数学库添加更多功能,并确保它保持良好的组织结构。例如,我们可以增加一些新的功能,如减法和除法函数。
math_functions.h (更新)
#ifndef MATH_FUNCTIONS_H
#define MATH_FUNCTIONS_H
// 函数声明
double add(double x, double y);
double subtract(double x, double y);
double multiply(double x, double y);
double divide(double x, double y);
#endif // MATH_FUNCTIONS_H
math_functions.cpp (更新)
#include "math_functions.h"
// 函数定义
double add(double x, double y) {
return x + y;
}
double subtract(double x, double y) {
return x - y;
}
double multiply(double x, double y) {
return x * y;
}
double divide(double x, double y) {
if (y == 0) {
throw std::invalid_argument("Division by zero is not allowed.");
}
return x / y;
}
我们增加了两个新的函数 subtract
和 divide
,同时在 divide
函数中添加了错误检查,以防止除数为零的情况。
更新主程序
现在我们可以更新 main.cpp
文件来测试所有新增的功能。
main.cpp (更新)
#include <iostream>
#include <stdexcept>
#include "math_functions.h"
int main() {
try {
double result_add = add(5.0, 3.0);
double result_subtract = subtract(5.0, 3.0);
double result_multiply = multiply(5.0, 3.0);
double result_divide = divide(5.0, 3.0);
std::cout << "Addition: " << result_add << std::endl;
std::cout << "Subtraction: " << result_subtract << std::endl;
std::cout << "Multiplication: " << result_multiply << std::endl;
std::cout << "Division: " << result_divide << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
这里我们在 main
函数中使用了 try-catch
块来捕获任何由 divide
函数抛出的异常。
使用构建系统
对于较大的项目,手动管理依赖关系和编译步骤可能会变得复杂。我们可以使用 CMake 来帮助我们自动化构建流程。以下是如何设置一个简单的 CMakeLists.txt 文件。
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(MathLib VERSION 1.0)
set(CMAKE_CXX_STANDARD 17)
add_executable(my_program main.cpp math_functions.cpp)
有了这个 CMake 配置文件后,你可以在终端中运行以下命令来配置和构建项目:
mkdir build
cd build
cmake ..
make
这将会在 build
目录下生成一个名为 my_program
的可执行文件。
通过这种方式,你可以继续扩展你的数学库,增加更多功能或优化现有功能,并保持代码的清晰和模块化。
演示案例2:学生信息管理系统
我们可以创建一个更加具体的案例,比如一个简单的学生信息管理系统。这个系统可以用来存储学生的个人信息以及他们的成绩,并提供基本的查询和修改功能。
设计模块
我们可以将这个系统分为以下几个模块:
- 数据模型:用于表示学生的信息和成绩。
- 数据访问层:用于管理学生数据的存储和检索。
- 业务逻辑层:实现对学生数据的操作,如添加、删除、更新和查询。
- 用户界面:用于与用户交互,接收输入并显示结果。
数据模型
首先,定义一个学生类来存储学生的相关信息。
student.h
#ifndef STUDENT_H
#define STUDENT_H
#include <string>
class Student {
public:
Student(const std::string& name, int age, const std::string& id);
~Student();
const std::string& getName() const { return name_; }
int getAge() const { return age_; }
const std::string& getId() const { return id_; }
private:
std::string name_;
int age_;
std::string id_;
};
#endif // STUDENT_H
student.cpp
#include "student.h"
#include <iostream>
Student::Student(const std::string& name, int age, const std::string& id)
: name_(name), age_(age), id_(id) {}
Student::~Student() {}
数据访问层
接下来,定义一个类来处理学生数据的存储。这里我们使用一个简单的向量来模拟数据库。
student_database.h
#ifndef STUDENT_DATABASE_H
#define STUDENT_DATABASE_H
#include <vector>
#include <memory>
#include "student.h"
class StudentDatabase {
public:
void addStudent(const std::shared_ptr<Student>& student);
void removeStudent(const std::string& id);
std::shared_ptr<Student> findStudentById(const std::string& id);
void updateStudent(const std::string& id, const std::shared_ptr<Student>& newStudent);
std::vector<std::shared_ptr<Student>> getAllStudents();
private:
std::vector<std::shared_ptr<Student>> students_;
};
#endif // STUDENT_DATABASE_H
student_database.cpp
#include "student_database.h"
void StudentDatabase::addStudent(const std::shared_ptr<Student>& student) {
students_.push_back(student);
}
void StudentDatabase::removeStudent(const std::string& id) {
students_.erase(std::remove_if(students_.begin(), students_.end(),
[id](const std::shared_ptr<Student>& s) { return s->getId() == id; }),
students_.end());
}
std::shared_ptr<Student> StudentDatabase::findStudentById(const std::string& id) {
for (auto& student : students_) {
if (student->getId() == id) {
return student;
}
}
return nullptr;
}
void StudentDatabase::updateStudent(const std::string& id, const std::shared_ptr<Student>& newStudent) {
auto it = std::find_if(students_.begin(), students_.end(),
[id](const std::shared_ptr<Student>& s) { return s->getId() == id; });
if (it != students_.end()) {
*it = newStudent;
}
}
std::vector<std::shared_ptr<Student>> StudentDatabase::getAllStudents() {
return students_;
}
用户界面
最后,我们创建一个简单的命令行界面来与用户交互。
main.cpp
#include <iostream>
#include <memory>
#include "student.h"
#include "student_database.h"
int main() {
StudentDatabase db;
while (true) {
std::cout << "1. Add Student" << std::endl;
std::cout << "2. Remove Student" << std::endl;
std::cout << "3. Find Student" << std::endl;
std::cout << "4. Update Student" << std::endl;
std::cout << "5. List All Students" << std::endl;
std::cout << "6. Exit" << std::endl;
std::cout << "Enter your choice: ";
int choice;
std::cin >> choice;
switch (choice) {
case 1: {
std::string name, id;
int age;
std::cout << "Enter name: ";
std::cin >> name;
std::cout << "Enter age: ";
std::cin >> age;
std::cout << "Enter ID: ";
std::cin >> id;
auto student = std::make_shared<Student>(name, age, id);
db.addStudent(student);
break;
}
case 2: {
std::string id;
std::cout << "Enter ID to remove: ";
std::cin >> id;
db.removeStudent(id);
break;
}
case 3: {
std::string id;
std::cout << "Enter ID to find: ";
std::cin >> id;
auto student = db.findStudentById(id);
if (student) {
std::cout << "Name: " << student->getName() << std::endl;
std::cout << "Age: " << student->getAge() << std::endl;
std::cout << "ID: " << student->getId() << std::endl;
} else {
std::cout << "Student not found." << std::endl;
}
break;
}
case 4: {
std::string id;
std::cout << "Enter ID to update: ";
std::cin >> id;
auto student = db.findStudentById(id);
if (student) {
std::string newName, newId;
int newAge;
std::cout << "Enter new name: ";
std::cin >> newName;
std::cout << "Enter new age: ";
std::cin >> newAge;
std::cout << "Enter new ID: ";
std::cin >> newId;
auto newStudent = std::make_shared<Student>(newName, newAge, newId);
db.updateStudent(id, newStudent);
} else {
std::cout << "Student not found." << std::endl;
}
break;
}
case 5: {
auto students = db.getAllStudents();
for (auto& student : students) {
std::cout << "Name: " << student->getName() << std::endl;
std::cout << "Age: " << student->getAge() << std::endl;
std::cout << "ID: " << student->getId() << std::endl;
std::cout << "-----------------" << std::endl;
}
break;
}
case 6: {
return 0;
}
default:
std::cout << "Invalid choice. Please try again." << std::endl;
}
}
return 0;
}
编译和运行
使用 CMake 构建这个项目,可以在 CMakeLists.txt
中添加如下内容:
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(StudentInfoSystem VERSION 1.0)
set(CMAKE_CXX_STANDARD 17)
add_executable(student_info_system main.cpp student.cpp student_database.cpp)
target_include_directories(student_info_system PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
然后,在终端中运行以下命令:
mkdir build
cd build
cmake ..
make
./student_info_system
这样就建立了一个简单的学生信息管理系统,可以用来管理和查询学生的相关信息。你可以根据需要进一步扩展这个系统的功能,比如增加成绩管理、持久化存储等功能。
案例扩展
让我们继续扩展这个学生信息管理系统,使其更加强大和实用。我们将添加成绩管理和持久化存储的功能。
成绩管理
我们将为每个学生添加一个成绩记录的功能,可以通过添加新的类来管理成绩,并在学生类中添加对成绩的引用。
grade.h
#ifndef GRADE_H
#define GRADE_H
#include <string>
class Grade {
public:
Grade(const std::string& subject, double score);
~Grade();
const std::string& getSubject() const { return subject_; }
double getScore() const { return score_; }
private:
std::string subject_;
double score_;
};
#endif // GRADE_H
grade.cpp
#include "grade.h"
Grade::Grade(const std::string& subject, double score)
: subject_(subject), score_(score) {}
Grade::~Grade() {}
student.h (更新)
#ifndef STUDENT_H
#define STUDENT_H
#include <string>
#include <vector>
#include <memory>
#include "grade.h"
class Student {
public:
Student(const std::string& name, int age, const std::string& id);
~Student();
const std::string& getName() const { return name_; }
int getAge() const { return age_; }
const std::string& getId() const { return id_; }
void addGrade(const std::shared_ptr<Grade>& grade);
std::vector<std::shared_ptr<Grade>> getGrades() const;
private:
std::string name_;
int age_;
std::string id_;
std::vector<std::shared_ptr<Grade>> grades_;
};
#endif // STUDENT_H
student.cpp (更新)
#include "student.h"
Student::Student(const std::string& name, int age, const std::string& id)
: name_(name), age_(age), id_(id) {}
Student::~Student() {}
void Student::addGrade(const std::shared_ptr<Grade>& grade) {
grades_.push_back(grade);
}
std::vector<std::shared_ptr<Grade>> Student::getGrades() const {
return grades_;
}
持久化存储
为了实现持久化存储,我们可以使用文件系统来保存学生的信息。这里我们将使用简单的文本文件来保存数据。
storage.h
#ifndef STORAGE_H
#define STORAGE_H
#include <fstream>
#include <sstream>
#include <vector>
#include <memory>
#include "student.h"
class Storage {
public:
static bool saveStudentsToFile(const std::vector<std::shared_ptr<Student>>& students, const std::string& filename);
static std::vector<std::shared_ptr<Student>> loadStudentsFromFile(const std::string& filename);
};
#endif // STORAGE_H
storage.cpp
#include "storage.h"
#include <iostream>
bool Storage::saveStudentsToFile(const std::vector<std::shared_ptr<Student>>& students, const std::string& filename) {
std::ofstream file(filename);
if (!file.is_open()) {
return false;
}
for (const auto& student : students) {
file << student->getName() << ' ' << student->getAge() << ' ' << student->getId() << '\n';
for (const auto& grade : student->getGrades()) {
file << grade->getSubject() << ' ' << grade->getScore() << '\n';
}
file << "END\n";
}
return true;
}
std::vector<std::shared_ptr<Student>> Storage::loadStudentsFromFile(const std::string& filename) {
std::ifstream file(filename);
std::vector<std::shared_ptr<Student>> students;
if (!file.is_open()) {
return students;
}
std::string line;
while (std::getline(file, line)) {
if (line == "END") {
continue;
}
if (students.empty() || !students.back()->getGrades().empty()) {
std::istringstream iss(line);
std::string name;
int age;
std::string id;
iss >> name >> age >> id;
auto student = std::make_shared<Student>(name, age, id);
students.push_back(student);
} else {
std::istringstream iss(line);
std::string subject;
double score;
iss >> subject >> score;
auto grade = std::make_shared<Grade>(subject, score);
students.back()->addGrade(grade);
}
}
return students;
}
更新主程序
我们需要更新 main.cpp
来集成成绩管理和持久化存储的功能。
main.cpp (更新)
#include <iostream>
#include <memory>
#include <vector>
#include "student.h"
#include "student_database.h"
#include "storage.h"
int main() {
StudentDatabase db;
const std::string filename = "students.dat";
// Load data from file if exists
auto loadedStudents = Storage::loadStudentsFromFile(filename);
for (const auto& student : loadedStudents) {
db.addStudent(student);
}
while (true) {
std::cout << "1. Add Student" << std::endl;
std::cout << "2. Remove Student" << std::endl;
std::cout << "3. Find Student" << std::endl;
std::cout << "4. Update Student" << std::endl;
std::cout << "5. List All Students" << std::endl;
std::cout << "6. Add Grade" << std::endl;
std::cout << "7. Save and Exit" << std::endl;
std::cout << "Enter your choice: ";
int choice;
std::cin >> choice;
switch (choice) {
case 1: {
std::string name, id;
int age;
std::cout << "Enter name: ";
std::cin >> name;
std::cout << "Enter age: ";
std::cin >> age;
std::cout << "Enter ID: ";
std::cin >> id;
auto student = std::make_shared<Student>(name, age, id);
db.addStudent(student);
break;
}
case 2: {
std::string id;
std::cout << "Enter ID to remove: ";
std::cin >> id;
db.removeStudent(id);
break;
}
case 3: {
std::string id;
std::cout << "Enter ID to find: ";
std::cin >> id;
auto student = db.findStudentById(id);
if (student) {
std::cout << "Name: " << student->getName() << std::endl;
std::cout << "Age: " << student->getAge() << std::endl;
std::cout << "ID: " << student->getId() << std::endl;
std::cout << "Grades:" << std::endl;
for (const auto& grade : student->getGrades()) {
std::cout << "Subject: " << grade->getSubject() << ", Score: " << grade->getScore() << std::endl;
}
} else {
std::cout << "Student not found." << std::endl;
}
break;
}
case 4: {
std::string id;
std::cout << "Enter ID to update: ";
std::cin >> id;
auto student = db.findStudentById(id);
if (student) {
std::string newName, newId;
int newAge;
std::cout << "Enter new name: ";
std::cin >> newName;
std::cout << "Enter new age: ";
std::cin >> newAge;
std::cout << "Enter new ID: ";
std::cin >> newId;
auto newStudent = std::make_shared<Student>(newName, newAge, newId);
db.updateStudent(id, newStudent);
} else {
std::cout << "Student not found." << std::endl;
}
break;
}
case 5: {
auto students = db.getAllStudents();
for (auto& student : students) {
std::cout << "Name: " << student->getName() << std::endl;
std::cout << "Age: " << student->getAge() << std::endl;
std::cout << "ID: " << student->getId() << std::endl;
std::cout << "Grades:" << std::endl;
for (const auto& grade : student->getGrades()) {
std::cout << "Subject: " << grade->getSubject() << ", Score: " << grade->getScore() << std::endl;
}
std::cout << "-----------------" << std::endl;
}
break;
}
case 6: {
std::string id, subject;
double score;
std::cout << "Enter ID of the student: ";
std::cin >> id;
std::cout << "Enter subject: ";
std::cin >> subject;
std::cout << "Enter score: ";
std::cin >> score;
auto student = db.findStudentById(id);
if (student) {
auto grade = std::make_shared<Grade>(subject, score);
student->addGrade(grade);
} else {
std::cout << "Student not found." << std::endl;
}
break;
}
case 7: {
auto students = db.getAllStudents();
Storage::saveStudentsToFile(students, filename);
return 0;
}
default:
std::cout << "Invalid choice. Please try again." << std::endl;
}
}
return 0;
}
编译和运行
使用 CMake 构建这个项目,可以在 CMakeLists.txt
中添加如下内容:
cmake_minimum_required(VERSION 3.10)
project(StudentInfoSystem VERSION 1.0)
set(CMAKE_CXX_STANDARD 17)
add_executable(student_info_system main.cpp student.cpp student_database.cpp grade.cpp storage.cpp)
target_include_directories(student_info_system PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
然后,在终端中运行以下命令:
mkdir build
cd build
cmake ..
make
./student_info_system
现在你有了一个更加完整的学生信息管理系统,它可以管理学生的个人信息和成绩,并能够将数据保存到文件中。你可以根据需要继续扩展和改进这个系统。
————————————————
最后我们放松一下眼睛