using std::string; using Distribution = std::uniform_int_distribution<size_t>; using Subject = string; // A course subject using Subjects = std::vector<Subject>; // A vector of subjects using Group = std::set<std::weak_ptr<Student>, std::owner_less<std::weak_ptr<Student>>>; // A student group for a subject using Students = std::vector<std::shared_ptr<Student>>; // All the students using Course = std::pair<Subject, Group>; // A pair representing a course using Courses = std::map<Subject, Group>; // The container for courses这些别名不是必需的,但它们可以使代码更加简洁。
// Student.h
// Student class definition for Ex5_02 - unchanged from Ex5_01
#ifndef STUDENT_H
#define STUDENT_H
#include <string> // For string class
#include <ostream> // For output streams
class Student
{
private:
std::string first {};
std::string second {};
public:
Student(const std::string& name1, const std::string& name2) : first (name1), second (name2){}
Student(Student&& student) : first(std::move(student.first)), second(std::move(student.second)){} // Move constructor
Student(const Student& student) : first(student.first), second(student.second){} // Copy constructor
Student() {} // Default constructor
// Less-than operator
bool operator<(const Student& student) const
{
return second < student.second || (second == student.second && first < student.first);
}
friend std::ostream& operator<<(std::ostream& out, const Student& student);
};
// Insertion operator overload
inline std::ostream& operator<<(std::ostream& out, const Student& student)
{
out << student.first + " " + student.second;
return out;
}
#endif
Student 只有两个数据成员,用来保存学生的名和姓。Student 对象最初保存在 vector 容器中,因此需要定义默认的构造函数。这里有拷贝构造函数和移动构造函数,后者可以在适当时避免对象的副本。因为 Student 会被保存在 set 容器中,用来表示不同学科的课程,所以需要定义小于运算符。这里用友元函数重载了一个流插入运算符来辅助输出。
Students create_students()
{
Students students;
string first_names[] {"Ann", "Jim", "Eve", "Dan", "Ted"};
string second_names[] {"Smith", "Jones", "Howe", "Watt", "Beck"};
for(const auto& first : first_names)
for(const auto& second : second_names)
{
students.emplace_back(std::make_shared<Student>(first, second));
}
return students;
}
这里用 using 语句为 Student 对象的 vector 容器定义了一个别名 Students。函数用两个数组中的元素组合出了所有可能的姓名,在局部 students 容器中创建 Student 对象。外循环对名进行迭代,内循环在给定的名后添加姓。因此我们会有 25 名学生。我们可以按如下方式调用函数来创建 students:Students students = create_students();vector 容器有移动构造函数,因此编译器会移动而不是拷贝返回的局部 students。上面的语句会调用 vector<Student> 的移动赋值运算符来移动 create_students() 的返回值,因此不会拷贝这个 vector 及其元素。这里 students 的类型是显式的,也可以使用 auto,因为编译器可以从 create_students() 的返回类型中推导出 students 的类型。
static std::default_random_engine gen_value;default_random_engine 和 uniforai_int_distribution 类型都定义在 random 头文件中,分布对象是一个函数对象,它可以创建分布内的随机数,可以将随机数创建器对象作为参数传给分布对象的成员函数 opemtor()()。这里我们定义了一个函数,用来为指定学科随机创 建一组学生:
Group make_group(const Students& students, size_t group_size, const Distribution& choose_student)
{
Group group; // The group of students for a subject
// Select students for the subject group
size_t count {}; // No. of students in the group
std::pair<Group::iterator, bool> pr; // Stores return from inserting a student
// Insert a random student into the group until there are group_size students in it
while(count < group_size)
{ // Increment count for a successful insertion...
if(group.insert(students[choose_student(gen_value)]).second) ++count;
}
return group;
}
函数的第一个参数是一个包含 Student 对象的 vector,第二个参数是组中要求的学生个数,最后一个参数是分布的索引值,用来选择随机创建的学生。
Subjects subjects {"Biology", "Physics", "Chemistry", "Mathematics", "Astronomy","Drama", "Politics", "Philosophy", "Economics"};
Subjects 是 vector<Subject> 的别名,Subject 是 string 的别名,因而 subjects 是一个包含 string 对象的 vector 容器。我们会在 map<Subject,Group> 中保存一些课程,每门学科的键都是 subjects 容器中唯一的 Subject 对象。map<Subject,Group> 的别名为 Courses,因此我们可以像下面这样定义一个包含全部课程的容器:Courses courses; // All the courses with subject keys这里定义了每个学生需要学习的最少学科数目。在创建某个学科的学生集合时,我们也对每个课程组的初始大小进行了限制:
size_t min_subjects {4}; // Minimum number of Subjects per student
size_t min_group {min_subjects}; // Minimum no. of students per course
size_t max_group {(students.size()*min_subjects) / subjects.size()}; // Maximum initial students per course
这里随便选了 4 作为需要学习的最少学科数,而且将每组的最少学生数都设为相同的 值。如果所有学生都学习最少的学科,而且被平均分配到每组,那么每个学科组的最大学生数就是一个平均值。可以尝试修改这些参数,看看它们对于学生分配有什么影响。
Distribution group_size {min_group, max_group}; // Distribution for students per course
Distribution choose_student {0, students.size() - 1}; // Random student selector
group_size 可以创建 min_group?max_group 范围内的随机数。类似的,choose_student 分布可以创建 students 中的有效索引值。
Distribution choose_course {0, subjects.size() - 1}; // Random course selector
上面的代码会创建 subjects 容器的一个有效索引值,可以用它来选择课程。
for(const auto& subject : subjects)
courses.emplace(subject, make_group(students, group_size(gen_value), choose_student));
这个循环体中的语句看起来确实很长。调用 course 容器的 emplace() 可以在适当的位置创建元素。每个元素都是一个 pair<Subject,Group> 对象,因此 emplace() 的参数必须是一个 Subject 对象和一个 Group 对象,emplace() 函数会把它们传入 pair 的构造函数。Emplace() 第一个参数是循环变量 subject,因为循环迭代器需要遍历 subjects。第二个参数是由 make_group() 返回的。make_group() 的第一个参数是一个 vector 容器 students,第二个参数通过将随机数创建器传入函数对象 group_size() 来创建随机数,用它作为组的大小。第三个参数是一个用于选择学生的分布对象。
for(const auto& student : students)
{
// Verify the minimum number of Subjects has been met
// Count how many Subjects the student is on
size_t course_count = std::count_if(std::begin(courses), std::end(courses), [&student](const Course& course)
{ return course.second.count(student); });
if(course_count >= min_subjects) continue; // On to the next student Minimum no. of Subjects not signed up for
size_t additional {min_subjects - course_count}; // Additional no. of Subjects needed
if(!course_count) // If none have been chosen...
std::cout << *student << " is work-shy, having signed up for NO Subjects!\n";
else // Some have - but E for effort
std::cout << *student << " is only signed up for " << course_count << " Subjects!\n";
std::cout << "Registering " << *student << " for " << additional<< " more course" << (additional > 1 ? "s" : "") << ".\n\n";
// Register for additional Subjects up to the minimum
while(course_count < min_subjects)
if((courses.find(subjects[choose_course(gen_value)])->second.insert(student)).second)
++course_count;
}
外层循环遍历 vector 中的 Student 对象。count_if() 算法用来计算每个学生已报名参加的课程数。
// List_Courses.h
// Function object to output the students in a group for Ex5_02
#ifndef LIST_COURSE_H
#define LIST_COURSE_H
#include <iostream> // For standard streams
#include <string> // For string class
#include <set> // For set container
#include <algorithm> // For copy()
#include <iterator> // For ostream_iterator
#include "Student.h"
using Subject = std::string; // A course subject
using Group = std::set<std::weak_ptr<Student>, std::owner_less<std::weak_ptr<Student>>>; // A student group for a subject
using Course = std::pair<Subject, Group>; // A pair representing a course
class List_Course
{
public:
void operator()(const Course& course)
{
std::cout << "\n\n" << course.first << " " << course.second.size() << " students:\n ";
std::copy(std::begin(course.second), std::end(course.second), std::ostream_iterator<std::weak_ptr<Student>>(std::cout, " "));
}
};
inline std::ostream& operator<<(std::ostream& out, const std::weak_ptr<Student>& wss)
{
out << *wss.lock();
return out;
}
#endif
List_Course 类的成员函数 operator()() 的参数是一个 Course 对象的引用,这个对象的类型是 pair<string,set<Student>>。这个函数会输出课程的一些信息,这些信息包括 course 的第一个成员,以及通过 pair 的第二个成员的成员函数 size() 得到的学习这门课程的学生数。前两个参数分别是 set<Student> 容器的开始迭代器和结束迭代器,这两个迭代器都是从 course 的第二个成员得到的。std::for_each(std::begin(courses), std::end(courses), List_Course());for_each 算法会将第三个参数指定的函数对象应用到前两个参数所指定范围内的所有元素上。前两个参数定义的范围相当于所有的课程,因此会以课程为参数连续调用 List_Courses()()。结果会将每门课程的全部学生输出到 cout。
std::for_each(std::begin(courses), std::end(courses), [](const Course& course){
std::cout << "\n\n" << course.first << " " << course.second.size() << " students:\n ";
std::copy(std::begin(course.second), std::end(course.second), std::ostream_iterator<std::weak_ptr<Student>>(std::cout, " "));
});
我们在这个示例中定义 List_Course 只是为了演示如何定义它,但在这个程序中需要多次使用这个函数对象,使用函数就比使用 lambda 表达式简单一些。
// Registering smart pointers to students on Subjects
#include <iostream> // For standard streams
#include <string> // For string class
#include <map> // For map container
#include <set> // For set container
#include <vector> // For vector container
#include <random> // For random number generation
#include <algorithm> // For for_each(), count_if()
#include <memory> // For shared_ptr and weak_ptr
#include "Student.h"
#include "List_Course.h"
using std::string;
using Distribution = std::uniform_int_distribution<size_t>;
using Subject = string; // A course subject
using Subjects = std::vector<Subject>; // A vector of subjects
using Group = std::set<std::weak_ptr<Student>, std::owner_less<std::weak_ptr<Student>>>; // A student group for a subject
using Students = std::vector<std::shared_ptr<Student>>; // All the students
using Course = std::pair<Subject, Group>; // A pair representing a course
using Courses = std::map<Subject, Group>; // The container for courses
static std::default_random_engine gen_value;
// Creates a vector of smart pointers to students
Students create_students()
{
Students students;
string first_names[] {"Ann", "Jim", "Eve", "Dan", "Ted"};
string second_names[] {"Smith", "Jones", "Howe", "Watt", "Beck"};
for(const auto& first : first_names)
for(const auto& second : second_names)
{
students.emplace_back(std::make_shared<Student>(first, second));
}
return students;
}
// Create a group of students for a subject
Group make_group(const Students& students, size_t group_size, const Distribution& choose_student)
{
Group group; // The group of students for a subject
// Select students for the subject group
size_t count {}; // No. of students in the group
std::pair<Group::iterator, bool> pr; // Stores return from inserting a student
// Insert a random student into the group until there are group_size students in it
while(count < group_size)
{ // Increment count for a successful insertion...
if(group.insert(students[choose_student(gen_value)]).second) ++count;
}
return group;
}
int main()
{
Students students = create_students();
Subjects subjects {"Biology", "Physics", "Chemistry", "Mathematics", "Astronomy","Drama", "Politics", "Philosophy", "Economics"};
Courses courses; // All the courses with subject keys
size_t min_subjects {4}; // Minimum number of Subjects per student
size_t min_group {min_subjects}; // Minimum no. of students per course
size_t max_group {(students.size()*min_subjects) / subjects.size()}; // Maximum initial students per course
// Create groups of students for each subject
Distribution group_size {min_group, max_group}; // Distribution for students per course
Distribution choose_student {0, students.size() - 1}; // Random student selector
for(const auto& subject : subjects)
courses.emplace(subject, make_group(students, group_size(gen_value), choose_student));
Distribution choose_course {0, subjects.size() - 1}; // Random course selector
// Every student must attend a minimum number of Subjects...
// ...but students being students we must check...
for(const auto& student : students)
{ // Verify the minimum number of Subjects has been met
// Count how many Subjects the student is on
size_t course_count = std::count_if(std::begin(courses), std::end(courses), [&student](const Course& course)
{ return course.second.count(student); });
if(course_count >= min_subjects) continue; // On to the next student
// Minimum no. of Subjects not signed up for
size_t additional {min_subjects - course_count}; // Additional no. of Subjects needed
if(!course_count) // If none have been chosen...
std::cout << *student << " is work-shy, having signed up for NO Subjects!\n";
else // Some have - but E for effort
std::cout << *student << " is only signed up for " << course_count << " Subjects!\n";
std::cout << "Registering " << *student << " for " << additional
<< " more course" << (additional > 1 ? "s" : "") << ".\n\n";
// Register for additional Subjects up to the minimum
while(course_count < min_subjects)
if((courses.find(subjects[choose_course(gen_value)])->second.insert(student)).second) ++course_count;
}
// Output the students attending each course
std::for_each(std::begin(courses), std::end(courses), List_Course());
std::cout << std::endl;
}
由于程序输出篇幅很长,此处不再列出,读者可自行复制代码查看运行结果。
版权说明:Copyright © 广州松河信息科技有限公司 2005-2025 版权所有 粤ICP备16019765号
广州松河信息科技有限公司 版权所有