程序要求
本项目基于 Project 5 的场景,要求实现:
- 使用自定义动态数组
StudentVector保存Student*。 - 使用运算符重载:
operator<<输出组统计,operator+=合并两个组。 - 在
World::DoSimulation()中实现每日 20% 拆分、20% 合并的行为并输出模拟结果。
目录结构
Decision.h/Decision.C— 决策逻辑Student.h/Student.C— 学生对象StudentVector.h/StudentVector.C— 自定义动态数组(存放Student*)Group.h/Group.C— 小组实现(含Split()、operator+=、operator<<)Weather.h/Weather.C— 天气状态与更新World.h/World.C— 世界与仿真逻辑main.C— 程序入口(生成可执行main)Makefile— 构建规则README.md— 本说明文档
构建与运行
在项目根目录运行:
make clean
make
./main
或使用 Makefile 的快捷目标:
make
make run
程序默认运行 30 天的模拟并将输出到标准输出。程序使用 time(NULL) 初始化随机种子,若需可修改 main.C 以接受命令行种子。
关键实现要点
-
StudentVector
- 内部使用
Student** p动态分配,支持自动扩容。 - 提供
Push(Student*)、operator[](size_t)(含 const 版本)、Len()与Truncate()。 - Student 的内存所有权由持有该指针的
Group管理;StudentVector本身不删除元素。
- 内部使用
-
Group
- 使用
StudentVector students存放学生指针,int* Decisions保存每次决策结果。 Split():把后半部分学生指针移动到新创建的Group,并对两组重新分配Decisions缓冲区。operator+=:将另一个组的所有学生指针移动进本组,并对被合并组执行Truncate(0),避免重复释放。operator<<:打印“总人数 / play / not play / ID 列表”,替代旧的Report()。
- 使用
-
World::DoSimulation()
- 每天按概率先尝试拆分或合并:
- 随机数
< 20→ 拆分某组(调用Split()) - 随机数在
[20,40)→ 随机挑选两组并使用group1 += group2合并,随后从数组中移除并delete被合并组
- 随机数
- 然后更新天气并调用每个组的
GroupDecision(weather),最后用operator<<输出。
- 每天按概率先尝试拆分或合并:
内存与安全注意
- 合并与拆分采用指针移动策略,保证每个
Student*在任意时刻只有一个Group持有并负责释放。 - 在合并后会把被合并组的学生指针清空(
Truncate(0)),因此被合并组析构时不会释放已转移的学生。
调试与内存检查
- 在 Linux 环境可使用
valgrind检查内存泄漏:
valgrind --leak-check=full --show-leak-kinds=all ./main
(macOS 原生对 valgrind 支持有限,常见做法是在 Linux 下运行或使用 macOS 的 Instruments 等工具。)
变更对照(相对 Project 5 思路)
- 移除对
./Project5的依赖,所有源码位于根目录 - 用
StudentVector替代Group里原生数组,使用运算符重载完成报告与合并 World中加入拆分 / 合并逻辑,遵循 20% 概率规则
源代码
main.C
#include
<cstdlib>
#include
<ctime>
#include
<iostream>
#include "World.h"
int main(int argc, char** argv) {std::srand(std::time(nullptr));
World world(20);
world.DoSimulation();
return 0;
}
World.h
#ifndef WORLD_H
#define WORLD_H
#include "Group.h"
#include "Weather.h"
class World {
private:
Group** groups;
int count;
Weather* w;
public:
World(int c);
~World();
void DoSimulation();};
#endif
World.C
#include "World.h"
#include
<iostream>
#include
<cstdlib>
World::World(int c) : count(c) {groups = new Group*[c];
for (int i = 0; i < c; i++) {groups[i] = new Group(i * 30 + 1, 30);
}
w = new Weather();}
World::~World() {for (int i = 0; i < count; i++) {delete groups[i];
}
delete[] groups;
delete w;
}
void World::DoSimulation() {for (int day = 1; day <= 30; day++) {
std::cout << "Day " << day << ":" << std::endl;
int dice = rand() % 100;
if (dice < 20 && count > 0) {int groupIndex = rand() % count;
Group* newGroup = groups[groupIndex]->Split();
if (newGroup != nullptr) {Group** temp = new Group*[count + 1];
for (int i = 0; i < count; i++) temp[i] = groups[i];
temp[count] = newGroup;
delete[] groups;
groups = temp;
count++;
}
} else if (dice < 40 && count > 1) {
// 20% chance to merge two random groups
int i = rand() % count;
int j = rand() % count;
while (j == i) j = rand() % count;
int g1 = i;
int g2 = j;
groups[g1]->operator+=(*groups[g2]);
// remove g2 from array and delete it
delete groups[g2];
Group** temp = new Group*[count - 1];
int idx = 0;
for (int k = 0; k < count; ++k) {if (k == g2) continue;
temp[idx++] = groups[k];
}
delete[] groups;
groups = temp;
count--;
}
w->UpdateWeather();
int weather = w->GetWeather();
for (int i = 0; i < count; i++) {std::cout << "Group " << (i + 1) << ": ";
groups[i]->GroupDecision(weather);
std::cout << *groups[i] << std::endl;
}
std::cout << std::endl;
}
}
Weather.h
#ifndef WEATHER_H
#define WEATHER_H
class Weather {
private:
int current;
public:
Weather();
int GetWeather();
void UpdateWeather();};
#endif
Weather.C
#include "Weather.h"
#include
<cstdlib>
Weather::Weather() {current = rand() % 5 + 1; // 1-5
}
int Weather::GetWeather() {return current;}
void Weather::UpdateWeather() {double dice = rand() % 100;
switch (current) {
case 5:
if (dice < 20) break;
else if (dice < 40) current = 1;
else if (dice < 60) current = 2;
else if (dice < 80) current = 3;
else current = 4;
break;
case 1:
if (dice < 10) break;
else if (dice < 40) current = 2;
else if (dice < 80) current = 5;
else current = 3;
break;
case 2:
if (dice < 20) break;
else if (dice < 50) current = 1;
else if (dice < 80) current = 5;
else if (dice < 90) current = 3;
else current = 4;
break;
case 3:
if (dice < 10) break;
else if (dice < 40) current = 5;
else if (dice < 60) current = 1;
else if (dice < 90) current = 4;
else current = 2;
break;
case 4:
if (dice < 20) break;
else if (dice < 40) current = 5;
else if (dice < 70) current = 1;
else if (dice < 90) current = 3;
else current = 2;
break;
}
}
StudentVector.h
#ifndef _STUDENT_VECTOR_H_
#define _STUDENT_VECTOR_H_
#include
<cstddef>
class Student; // forward declaration
class StudentVector{
Student** p;
size_t size;
size_t len;
public:
StudentVector();
~StudentVector();
void Clear();
void Push(Student* s);
Student*& operator[](size_t i);
Student* operator[](size_t i) const;
size_t Len() const { return len;}
void Truncate(size_t newLen);
};
#endif
StudentVector.C
#include "StudentVector.h"
#include "Student.h"
StudentVector::StudentVector() {
size = 10;
len = 0;
p = new Student*[size];
}
void StudentVector::Clear() {
// Only logical clear; does not delete Student* elements
len = 0;
}
StudentVector::~StudentVector() {delete [] p;
}
void StudentVector::Push(Student* s) {if (len == size - 1) {
size *= 2;
Student** temp = new Student*[size];
for (size_t i = 0; i < len; i++) temp[i] = p[i];
delete [] p;
p = temp;
}
p[len++] = s;
}
Student*& StudentVector::operator[](size_t i) {return p[i];
}
Student* StudentVector::operator[](size_t i) const {return p[i];
}
void StudentVector::Truncate(size_t newLen) {if (newLen < len) {for (size_t i = newLen; i < len; ++i) {p[i] = nullptr;
}
len = newLen;
}
}
Student.h
#ifndef STUDENT_H
#define STUDENT_H
#include "Decision.h"
class Student {
private:
int ID;
Decision* decision;
public:
Student(int k);
~Student();
int MakeDecision(int weather);
int GetID();};
#endif
Student.C
#include "Student.h"
#include
<cstdlib>
Student::Student(int k) : ID(k) {double p = 0.1 + (rand() % 90) / 100.0; // random p between 0.1 and 0.99
decision = new Decision(p);
}
Student::~Student() {delete decision;}
int Student::MakeDecision(int weather) {return decision->MakeDecision(weather);
}
int Student::GetID() {return ID;}
Group.h
#ifndef GROUP_H
#define GROUP_H
#include "Student.h"
#include "StudentVector.h"
#include
<ostream>
class Group {
private:
StudentVector students;
int* Decisions;
public:
Group();
Group(int startingID, int c);
~Group();
void GroupDecision(int weather);
Group* Split();
int GetCount() const { return static_cast
<int>(students.Len()); }
Group& operator+=(Group& other);
friend std::ostream& operator<<(std::ostream& os, const Group& g);
};
#endif
Group.C
#include "Group.h"
#include
<iostream>
#include
<cstdlib>
Group::Group() : Decisions(nullptr) {}
Group::Group(int startingID, int c) : Decisions(nullptr) {Decisions = new int[c];
for (int i = 0; i < c; i++) {students.Push(new Student(startingID + i));
}
}
Group::~Group() {int cnt = GetCount();
for (int i = 0; i < cnt; i++) {if (students[(size_t)i] != nullptr)
delete students[(size_t)i];
}
delete [] Decisions;}
void Group::GroupDecision(int weather) {int cnt = GetCount();
delete [] Decisions;
Decisions = new int[cnt];
for (int i = 0; i < cnt; i++) {Decisions[i] = students[(size_t)i]->MakeDecision(weather);
}
}
Group* Group::Split() {int cnt = GetCount();
if (cnt < 10) return nullptr;
int newCount = cnt / 2;
int remainingCount = cnt - newCount;
Group* newGroup = new Group();
// move second half to newGroup
for (int i = remainingCount; i < cnt; ++i) {newGroup->students.Push(students[(size_t)i]);
}
// truncate current
students.Truncate((size_t)remainingCount);
// reset decisions for both groups
delete [] Decisions;
Decisions = new int[GetCount() > 0 ? GetCount() : 1];
delete [] newGroup->Decisions;
newGroup->Decisions = new int[newGroup->GetCount() > 0 ? newGroup->GetCount() : 1];
return newGroup;
}
Group& Group::operator+=(Group& other) {int otherCnt = other.GetCount();
for (int i = 0; i < otherCnt; ++i) {students.Push(other.students[(size_t)i]);
}
// make other empty so its destructor doesn't delete moved students
other.students.Truncate(0);
// update decisions buffer size lazily in next GroupDecision
return *this;
}
std::ostream& operator<<(std::ostream& os, const Group& g) {int cnt = g.GetCount();
int playCount = 0;
for (int i = 0; i < cnt; i++) {if (g.Decisions && g.Decisions[i] == 0) playCount++;
}
os << cnt << " students, " << playCount << " play, "
<< (cnt - playCount) << " not play" << std::endl;
os << " ID to play: ";
bool first = true;
for (int i = 0; i < cnt; i++) {if (g.Decisions && g.Decisions[i] == 0) {if (!first) os << " ";
os << g.students[(size_t)i]->GetID();
first = false;
}
}
return os;
}
Decision.h
#ifndef DECISION_H
#define DECISION_H
class Decision {
private:
double x;
public:
Decision(double p);
int MakeDecision(int weather);
};
#endif
Decision.C
#include "Decision.h"
#include
<cstdlib>
Decision::Decision(double p) : x(p) {}
int Decision::MakeDecision(int weather) {double dice = rand() % 100;
double threshold = x * 100.0;
if (dice < threshold) return 1;
if (weather == 5) return 0;
if (weather < 3) {dice = rand() % 100;
if (dice >= threshold / 2.0) return 1;
if (weather == 1) {dice = rand() % 100;
if (dice < threshold) return 0;
else return 1;
} else {dice = rand() % 100;
if (dice < threshold / 2.0) return 1;
else return 0;
}
} else {dice = rand() % 100;
if (dice < threshold) return 1;
if (weather == 3) {dice = rand() % 100;
if (dice < threshold) return 0;
else return 1;
} else {dice = rand() % 100;
if (dice < threshold / 2.0) return 1;
else return 0;
}
}
}
Makefile
CC = g++
CFLAGS = -g -w
TARGET = main
# Project6 sources in root
SRCS = \
Decision.C \
Student.C \
StudentVector.C \
Group.C \
Weather.C \
World.C
OBJS = $(SRCS:.C=.o) main.o
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $(TARGET) $(OBJS)
# Compile Project6 entry
main.o: main.C World.h
$(CC) $(CFLAGS) -c -o $@ $<
%.o: %.C
$(CC) $(CFLAGS) -c -o $@ $<
.PHONY: clean run
run: $(TARGET)
./$(TARGET)
clean:
rm -f $(TARGET) main *.o
正文完


