Project 6 — Operator Overload + StudentVector

231次阅读
没有评论

程序要求

本项目基于 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

正文完
 3
评论(没有评论)