文章目录
-
- 设计思路
- 成果展示
- 代码相关设计
- 小结
设计思路
例如:
1.项目总体需求分析:根据用户信息实现类似相亲网站上的男女匹配功能
2.项目功能设计:
用户参数设计:本项目男生设计三个参数——姓名,年龄,薪资
女生设计三个参数——姓名,年龄,颜值
信息导入功能:
a.首次使用,则用户通过控制台输入存储男女信息(用户也可直接创建txt文件进行信息录入,保证文件的名字一致即可)
b.二次使用,直接读取txt文件中的信息进行操作
自动匹配功能:系统根据男女信息参数进行匹配,并打印出全部配对结果,筛选出 彼此满意的异性信息
成果展示
所有会员信息展示
全部配对的结果
最满意的配对情况
代码相关设计
类的设计
- 根据用户分为男人和女人分别设计男人类Boy和女人类Girl
- 男人类女人类有共同的特性,因此根据继承的思想设计父类Single
- 系统整体的运行需要控制,设计一个控制类Database,进行信息的导入和全匹配以及最优匹配
- 整体设计思想:先将整体的接口和模块设计出来,然后再设计具体细节,不足之处进行补充
父类:Single.h
#pragma once
#include <iostream>
#include <vector>
using namespace std;
//boy和girl的父类
//男孩和女孩的共有属性 姓名和年龄
class Single {
public:
Single(string name, int age); //构造函数
string getName()const; //获取姓名
int getAge()const;//获取年龄
virtual int trait()const = 0; //虚函数 特性
protected:
string name;
int age;
};
Single.c
#include "Single.h"
Single::Single(string name = "", int age = 0)
: name(name), age(age) {}
string Single::getName()const {
return name;
}
int Single::getAge()const {
return age;
}
男孩类:Boy.h
#pragma once
#include <iostream>
#include <vector>
#include "Single.h"
using namespace std;
class Girl;
class Boy :public Single { //继承父类
public:
Boy(string name, int age, int salary); //构造函数
int trait()const { //男孩子特性返回薪水
return salary;
}
bool satisfied(const Girl& girl)const; //男孩子对女孩子是否满意
bool operator>(const Boy& boy)const;
static void inputBoys(vector<Boy>& boys);//控制台中转至容器,容器中转至文件
friend ostream& operator<<(ostream& os, const Boy& boy);//友元函数
private:
int salary;
};
ostream& operator<<(ostream& os, const Boy& boy);
Boy.c
#include "Boy.h"
#include <sstream>
#include <iomanip>
#include "Girl.h"
#define SALARY_COEFFICIENT 0.006 //薪水系数 110000 对应的女孩子颜值是60
//构造函数
Boy::Boy(string name = "", int age = 0, int salary = 0)
:Single(name, age), salary(salary) {}//若二者合一,则定义新变量时是否需要带括号??必须带括号,因为不存在默认构造函数!
//是否满意
bool Boy::satisfied(const Girl& girl)const
{
int satisfiedFaceScore = salary * SALARY_COEFFICIENT; //计算男孩子满意的颜值
if (salary * SALARY_COEFFICIENT > 100) satisfiedFaceScore = 100; //bug处理,颜值太高定义为100
if (girl.trait() >= satisfiedFaceScore) return true; //如果女孩子的特性(颜值) 大于等于 男孩子的满意度,则返回满意
return false;//否则,不满意
}
bool Boy::operator>(const Boy& boy)const //判断薪水高低
{
return salary > boy.salary;
}
void Boy::inputBoys(vector<Boy>& boys) //输入男孩子的信息
{
int n = 1;
while (1) {
string name;
int age;
int salary;
cout << "请输入第" << n << "位小哥哥的姓名(输入0结束):";
cin >> name;//需要另外定义形参,最后一并通过vector名.push_back存入,push_back意为推到最后面,每次加入的都位于最后,像排队一样
if (name == "0") break;
cout << "年龄:";
cin >> age;
cout << "月薪:";
cin >> salary;
boys.push_back(Boy(name, age, salary));//可不通过类型调用带参构造函数
n++;
}
}
//cout << 重载和比较 < 重载
ostream& operator<<(ostream& os, const Boy& boy) {//直接打印,无需返回stringstream.str()的函数中转
os << "男 姓名:" << setw(9) << left << boy.name
<< " 年龄:" << setw(4) << boy.age
<< "月薪:" << boy.salary << endl;
return os;
}
女孩类 Girl.h
#pragma once
#include <iostream>
#include <vector> //Vector容器
#include "Single.h"
using namespace std;
class Boy;
class Girl :public Single {
public:
Girl(string name = "", int age = 0, int faceScore = 0);
int trait()const {
return faceScore;
}
bool satisfied(const Boy& boy)const;
bool operator>(const Girl& girl)const;
friend ostream& operator<<(ostream& os, const Girl& girl);
static void inputGirls(vector<Girl>& girls);
private:
int faceScore;//颜值
};
ostream& operator<<(ostream& os, const Girl& girl);
Girl.c
#include "Girl.h"
#include <sstream>
#include <iomanip>
#define FACESCORE_COEFFICIENT 100
#include "Boy.h"
Girl::Girl(string name, int age, int faceScore)
:Single(name, age), faceScore(faceScore) {}
bool Girl::satisfied(const Boy& boy)const
{
if (boy.trait() >= faceScore * FACESCORE_COEFFICIENT) return true;//因为有boy的方法,所以要包括boy的头文件
return false;
}
bool Girl::operator>(const Girl& girl)const
{
return faceScore > girl.faceScore; //判断颜值高低
}
void Girl::inputGirls(vector<Girl>& girls)
{
int n = 1;
while (1) {
string name;
int age;
int faceScore;
cout << "请输入第" << n << "位小姐姐的姓名(输入0结束):";
cin >> name;//需要另外定义形参,最后一并通过vector名.push_back存入
if (name == "0") break; //结束跳出循环
cout << "年龄:";
cin >> age;
cout << "颜值:";
cin >> faceScore;
girls.push_back(Girl(name, age, faceScore));//可不通过类型调用带参构造函数
n++;
}
}
ostream& operator<<(ostream& os, const Girl& girl) { //重写<<
os << "女 姓名:" << setw(9) << left << girl.name//left=setiosflags(ios::left) //左对齐 设置输出宽度
<< " 年龄:" << setw(4) << girl.age
<< "颜值:" << girl.faceScore << endl;
return os;
}
控制类Database.h
#pragma once
#include "Boy.h"
#include "Girl.h"
/*
功能:
加载用户信息load()
实现自动配对automatch()
打印配对信息print()
数据:
男信息
女信息
*/
class Database
{
public:
Database();
//加载用户信息
void load(bool input = false) {
loadBoys(input);
loadGirls(input);
}
void print() const;//打印boys和allGirls中所有男女信息
void allMatch() const; //匹配
void allBestMatch() const;
private:
vector<Boy> boys;//vector每次用之前需要初始化为0吗?
vector<Girl>allGirls;
vector<Boy> newBoys;
vector<Girl> newGirls;
void loadBoys(bool input);//将文件中加载至vector
void loadGirls(bool input);
void saveBoys(const vector<Boy>& boys);//将vector中存入文件
void saveGirls(const vector<Girl>& allGirls);
void automatchBoy(const vector<Boy>& boys, const vector<Girl>& allGirls) const;
void automatchGirl(const vector<Boy>& boys, const vector<Girl>& allGirls) const;
void bestMatchBoy(const vector<Boy>& boys, const vector<Girl>& allGirls) const;
void bestMatchGirl(const vector<Boy>& boys, const vector<Girl>& allGirls) const;
};
Database.c
#include <fstream>
#include <string>
#include "Database.h"
#define BOY_FILE "boys.txt"
#define GIRL_FILE "girls.txt"
#define BOY_INPUT_INFO 3 //男士输入数据数目(姓名,年龄,月薪)
#define GIRL_INPUT_INFO 3 //女士输入数据数目(姓名,年龄,颜值)
string line(100, '-');
Database::Database()
{
}
void Database::allMatch() const {
cout << line << endl;
cout << "全部单身男女自动配对结果:" << endl;
automatchBoy(boys, allGirls);
cout << line << endl;
automatchGirl(boys, allGirls);
cout << line << endl;
}
void Database::allBestMatch() const
{
cout << line << endl;
cout << "全部单身男女最佳配对结果:" << endl;
bestMatchBoy(boys, allGirls);
cout << line << endl;
bestMatchGirl(boys, allGirls);
cout << line << endl;
}
void Database::bestMatchBoy(const vector<Boy>& boys, const vector<Girl>& allGirls) const {
for (unsigned int b = 0; b < boys.size(); b++) {
cout << boys[b] << " 的最佳配对女士为:" << endl;
const Girl* bestGirl = NULL;
for (unsigned int g = 0; g < allGirls.size(); g++) {
if (boys[b].satisfied(allGirls[g]) == true &&
allGirls[g].satisfied(boys[b]) == true) {//注意调用的是否是函数以及函数是否含参以及含参顺序
//cout << boys[b].getName() << "<-->" << allGirls[g].getName() << endl;如果只打印姓名,可能有重名,无法确定实际是哪位
//因为database与Boy和Girl类不存在继承关系,所以只能通过对象调用public方法
if (!bestGirl) {
bestGirl = &allGirls[g];
}
else if (allGirls[g] > *bestGirl) {
bestGirl = &allGirls[g];
}
}
}
if (bestGirl) {
cout << *bestGirl;
}
else {
cout << "配对失败!" << endl;
}
cout << endl;//一位男士的所有配对结束后空一行
}
}
void Database::bestMatchGirl(const vector<Boy>& boys, const vector<Girl>& allGirls) const {
for (unsigned int g = 0; g < allGirls.size(); g++) {
cout << allGirls[g] << " 的最佳配对男士为:" << endl;
const Boy* bestBoy = NULL;
for (unsigned int b = 0; b < boys.size(); b++) {
if (boys[b].satisfied(allGirls[g]) == true &&
allGirls[g].satisfied(boys[b]) == true) {//注意调用的是否是函数以及函数是否含参以及含参顺序
//cout << boys[b].getName() << "<-->" << allGirls[g].getName() << endl;如果只打印姓名,可能有重名,无法确定实际是哪位
//因为database与Boy和Girl类不存在继承关系,所以只能通过对象调用public方法
if (!bestBoy) {
bestBoy = &boys[b];
}
else if (boys[b] > *bestBoy) {//TO DO <运算符重载
bestBoy = &boys[b];
}
}
}
if (bestBoy) {
cout << *bestBoy;
}
else {
cout << "配对失败!" << endl;
}
cout << endl;//一位女士的所有配对结束后空一行
}
}
void Database::automatchBoy(const vector<Boy>& boys, const vector<Girl>& allGirls)const
{
//遍历两两配对
for (unsigned int b = 0; b < boys.size(); b++) {
cout << boys[b] << " 的配对女士有:" << endl;
for (unsigned int g = 0; g < allGirls.size(); g++) {
if (boys[b].satisfied(allGirls[g])
&& allGirls[g].satisfied(boys[b])) {//注意调用的是否是函数以及函数是否含参以及含参顺序
//cout << boys[b].getName() << "<-->" << allGirls[g].getName() << endl;如果只打印姓名,可能有重名,无法确定实际是哪位
//因为database与Boy和Girl类不存在继承关系,所以只能通过对象调用public方法
cout << allGirls[g];
}
}
cout << endl;//一位男士的所有配对结束后空一行
}
}
void Database::automatchGirl(const vector<Boy>& boys, const vector<Girl>& allGirls)const
{
//遍历两两配对
for (unsigned int g = 0; g < allGirls.size(); g++) {
cout << allGirls[g] << " 的配对男士有:" << endl;
for (unsigned int b = 0; b < boys.size(); b++) {
if (boys[b].satisfied(allGirls[g])
&& allGirls[g].satisfied(boys[b])) {//注意调用的是否是函数以及函数是否含参以及含参顺序
//cout << boys[b].getName() << "<-->" << allGirls[g].getName() << endl;如果只打印姓名,可能有重名,无法确定实际是哪位
//因为database与Boy和Girl类不存在继承关系,所以只能通过对象调用public方法
cout << boys[b];
}
}
cout << endl;//一位成女士的所有配对结束后空一行
}
}
void Database::print()const
{
string line(100, '-');//记得在初始化时这样写
cout << "男士信息:" << endl;
for (unsigned int i = 0; i < boys.size(); i++) {
cout << boys[i];
}
cout << "女士信息:" << endl;
for (unsigned int i = 0; i < allGirls.size(); i++) {
cout << allGirls[i];
}
cout << line << endl;
}
void Database::loadBoys(bool input)
{
//打开读文件
ifstream boyfile;
boyfile.open(BOY_FILE);
if (!boyfile.is_open()) {
//还没有此文件名的文件,需要用户输入,作为基础信息,存入vector->vector.vector中已有,不需要再从文件中读取
Boy::inputBoys(this->newBoys);
//将vector中的基础信息以description中的格式写入新建的文件,并结束加载
saveBoys(newBoys);
boyfile.close();
return;
}
if (input) {
Boy::inputBoys(this->newBoys);
saveBoys(newBoys);
}
while (1) {
//将读文件信息解析??为啥要解析嘞??已经存好了的啊,,是为了显示出来吗,要显示出来可以直接按存入的vector显示吗?不能
//因为vector掉电即失,需写入文件中转
//sscanf读取字符串会自动跳过回车符
//根据写的格式读取文件
/*ret << "男 姓名:" << setw(9) << left << name
<< " 年龄:" << setw(4) << age
<< "月薪:" << salary << endl;*/
string line;
getline(boyfile, line);
if (boyfile.eof()) {
break;
}
char name[32] = "";
int age, salary;
int ret = sscanf_s(line.c_str(), "男 姓名:%s 年龄:%d 月薪:%d", name, sizeof(name), &age, &salary);
if (ret < BOY_INPUT_INFO) {
cerr << "男士信息写入有误!" << endl;
exit(1);//视项目需求:break/exit
}
//并加载至vector以供后续配对
boys.push_back(Boy(name, age, salary));//string存入时最后记得加\0结束符,因为读取的时候要用char型读取,可以不加,因为格式里面专门加了空格隔开
}
boyfile.close();
}
//inputGirls:控制台->vector
//saveGirls:vector->文件
void Database::loadGirls(bool input)
{
ifstream girlfile;
girlfile.open(GIRL_FILE);
// 还没有此文件,需要用户输入基础信息
if (!girlfile.is_open()) {
Girl::inputGirls(this->newGirls);
//将输入的基础信息以description中的格式保存至文件尾部
saveGirls(newGirls);
girlfile.close();
return;
}
// 用户希望输入新信息
if (input) {
Girl::inputGirls(this->newGirls);
//将输入的基础信息以description中的格式保存至文件尾部
saveGirls(newGirls);
}
while (1) {
//将读文件信息解析??为啥要解析嘞??已经存好了的啊,,是为了显示出来吗,要显示出来可以直接按存入的vector显示吗?不能
//因为vector掉电即失,需写入文件中转
//sscanf读取字符串会自动跳过回车符
//根据写的格式读取文件
/* ret << "女 姓名:" << setw(9) << left << name
<< " 年龄:" << setw(4) << age
<< "颜值:" << faceScore << endl;*/
string line;
getline(girlfile, line);
if (girlfile.eof()) {
break;
}
char name[32] = "";
int age, faceScore;
int ret = sscanf_s(line.c_str(), "女 姓名:%s 年龄:%d 颜值:%d", name, sizeof(name), &age, &faceScore);
if (ret < GIRL_INPUT_INFO) {
cerr << "女士信息写入有误!" << endl;
exit(1);//视项目需求:break/exit
}
//并加载至vector以供后续配对
allGirls.push_back(Girl(name, age, faceScore));//string存入时最后记得加\0结束符,因为读取的时候要用char型读取,可以不加,因为格式里面专门加了空格隔开
}
girlfile.close();
}
void Database::saveBoys(const vector<Boy>& boys)
{
ofstream boyfile;
boyfile.open(BOY_FILE, ios::app);//尾部附加
if (!boyfile.is_open()) {
cerr << "写入:男士文件打开失败!" << endl;
exit(1);//exit还是break按项目需求
}
for (unsigned int i = 0; i < boys.size(); i++) {
boyfile << boys[i];
}
boyfile.close();
}
void Database::saveGirls(const vector<Girl>& allGirls)
{
ofstream girlfile;
girlfile.open(GIRL_FILE, ios::app);
if (!girlfile.is_open()) {
cerr << "写入:女士文件打开失败!" << endl;
exit(-1);
}
for (unsigned int i = 0; i < allGirls.size(); i++) {
girlfile << allGirls[i];
}
girlfile.close();
}
主函数 main.c
#include <iostream>
#include"Boy.h"
#include"Girl.h"
#include<vector>
#include "Database.h"
using namespace std;
int main() {
Database database;
database.load();
database.print();
database.allMatch();
database.allBestMatch();
return 0;
}
小结
本项目是C++的入门项目,主要的基于面向对象的思想,运用了C++的继承、重载,容器,友元函数等知识,初学者可以多加练习。