文章目录

  • 前言
  • 正文
    • 问题分析
    • 方法设计
      • 类构造函数
      • 主人:认养一只狗
      • 主人:狗的转让
      • 主人:遗弃狗
      • 主人:显示所有狗
      • 输出信息、更改信息
      • 狗:选择主人
    • 测试功能
    • 交互设计
      • 写在前面的一些解释
      • 显示所有主人、所有狗
      • 添加一只狗、添加一个主人
      • 编辑主人、编辑狗
      • 除去一只狗
      • 领养一只狗
      • 转移一只狗
      • 帮助菜单
      • 主函数
  • 后记

前言

欢迎收看年度热播大剧《C++、主人和狗》!

【注意】本篇博文仅代表个人思路,希望读者先有自己的思考避免先入为主。文章内容只是记录C++的初步学习过程,只是一些很简单的C语言内容,并不包含任何高级的数据存储方式或是查找算法;如有大佬光顾,还请多多指正(发抖…)。

欢迎批评指正、讨论交流!

一些链接
关于标题的抖机灵:点击观看《篱笆·女人和狗》 - 哔哩哔哩
源代码文件(链接不保证有效)20200506.cpp - 超星云盘

正文

对的,这又是一篇做作业的心路历程……话不多说,本周作业原文如下:

以下是教材“14.2 正确处理类的复合关系和继承关系”的部分内容:
https://2d.hep.com.cn/187722/17?from=groupmessage
在此基础上开发一个小区养狗管理程序,功能包括:

  1. 某主人认养一只狗
  2. 某主人把自己养的狗转让给别的主人
  3. 能正确显示某主人养了哪些狗,以及某狗的主人是谁

问题分析

老师称,布置这个作业的目的,是觉得书上虽然提到了“你中有我,我中有你”的处理办法(没有书的读者,相关内容可以参见作业原文里面的链接,内容是一样的),但是觉得需要进行实践才能更好掌握,所以布置了这样一个作业。

承接书上的内容,狗和主人相互要能找到彼此,这个用指针来实现;沿用书上假设的前提,每个主人最多只能养10条狗,所以我就用一个长度为10的指针数组来存储主人所养的狗。

然后,主人主要要有认养转让输出所养狗的信息等行为;狗要有输出主人信息的行为。

狗类命名为CDog,主人类命名为DogOwner,下面是类的定义:

class DogOwner; //需要事先声明,不然不能在CDog里面使用DogOwner的指针
class CDog
{private:char dog_name[32];    //狗的名字char dog_breed[32];   //狗的品种DogOwner * ptr_owner;  //指向主人的指针
public:CDog(const char* _name, const char* _breed, DogOwner * pm);void print_info(bool if_print_owner = false);void change_info(char* _name, char* _breed);//下面两个函数用来解决一些类外访问的问题(还有一个问题后面解释)void set_ptr_owner(DogOwner* pm){ptr_owner = pm;};DogOwner* get_ptr_owner(){return ptr_owner;};bool reassign_owner(DogOwner* pm); //狗重新选择主人(可选;实现时注意避免循环调用)
};class DogOwner
{private:char owner_name[32];CDog * dogs[10];int dogNum = 0;
public:DogOwner(const char * );void rename(char*);bool adopt_a_dog(CDog*);  //建立当前主人和一只现有狗的关系bool transfer_a_dog(CDog*, DogOwner* ); //实现狗关系的移交bool remove_a_dog(CDog*);  //解除当前主人与一只现有狗的关系void print_info();void view_dogs();
};

方法设计

类构造函数

DogOwner类,要有个关于名字的初始化吧;另外,得把dogs数组清零——也就是设成NULL——免得出一些意外的情况;设置dogNum这个变量,是为了在遍历过程中确立一个界限,并且在进行狗转移(人狗关系移交)的时候,可以预先判断是否可行。关于姓名的存储,我就只用了简单的字符数组(见前文类定义),读者也可以尝试使用string类。

其实可以不用dogNum,每次遍历整个dogs数组也是可以的,即找到值为NULL的地方插入,找不到就说明数组满了。

DogOwner::DogOwner(const char* str)
{strcpy(owner_name, str);//可以用for循环初始化,但是效率貌似不如memset函数//for (int i = 0; i < sizeof(dogs) / sizeof (CDog*); i++) dogs[i] = NULL;memset(dogs, 0, sizeof(dogs));dogNum = 0;
}

我的一些同学,想在DogOwner类初始化的时候,就带上“主人养的狗”,这样其实有一点点麻烦,因为狗的数目可能是3只,也可能是2只,并不固定:

可以用一个CDog*数组存下要传给对象的信息:

DogOwner::DogOwner(const char * name, CDog** dog_array){/* ... */for (int i = 0; i < 10; i++)if (dogs_array[i] != NULL) dogs[i] = dogs_array[i];
}
CDog d1("Kiki"), d2("Jimmy"), d3("Coco"), d4("Henry"), d5("Neko");
CDog* init_dogs[10] = {&d1, &d2};
DogOwner m1("Sandy",init_dogs);

也可以通过给构造函数设10个带缺省值的参数:

DogOwner::DogOwner(const char * name, CDog* d1 = NULL, CDog* d2 = NULL,/* ... */CDog* d10 = NULL);
DogOwner m1("Sam", &d3, &d4);

当然,也可以不设,直接用“领养”代替:

DogOwner m3("Susan");
m3.adopt_a_dog(&d5);

CDog类构造函数写起来可能会复杂一点,但其实就是考虑了初始主人的设定。

CDog::CDog(const char* _name, const char* _breed = "Unknown", DogOwner* pm = NULL)
{strcpy(dog_name, _name);strcpy(dog_breed, _breed);ptr_owner = NULL;if (pm != NULL) {bool if_adoption_success = pm->adopt_a_dog(this);if (if_adoption_success) {print_info(true); //输出狗的信息,参数true表示同时输出主人信息cout<<endl;cout<<"Dog initial owner set successfully! \n";ptr_owner = pm;}else {cout<<"Dog initial owner set failed! Owner set to [no one].\n";}}
}

主人:认养一只狗

目前这里只是从类方法设计的角度讨论。至于交互过程中的“认养一只狗”,我就放在后面再说吧。

那么下面开始写主人DogOwner类)的“认养一只(现有的)狗”,换句话说,就是主人处要有这只狗,狗也要认得这个主人——也就是建立主人和狗之间的关系

读者可以忽略的随笔:说到关系,我想到可以用二维数组储存两类事物之间的关系;至于一对多的关系,貌似可以用树之类的数据结构吧(一个朋友也跟我说到了这个,然而我还不会……就姑且用数组这种简陋的方式吧)

bool DogOwner::adopt_a_dog(CDog* pd)
{//被认养的这只狗应该是没有主人的if (pd->get_ptr_owner() != NULL) {cout<<"Dog '";pd->print_info();cout<<"' already belongs to ";pd->get_ptr_owner()->print_info();cout<<".\n";return false;}//已经养了超过了10只狗,不能认养if (dogNum >= 10) {cout<<"The owner '"<<owner_name<<"' already has 10 dogs!\n";return false;}else {dogs[dogNum++] = pd;      //主人处增添这只狗pd->set_ptr_owner(this);  //狗承认主人return true;}
}

主人:狗的转让

沿用上面的说法,这应该就是两个类之间关系的转让。转让的过程大概要有这样几步:

  • 旧的主人删除掉这条狗的信息;
  • 新的主人添加这条狗的信息;
  • 狗承认新主人。

如果我想m1把指针d2指向的狗转给m2,我希望使用起来可以是下面这样子:

DogOwner m1("Jason");
DogOwner m2("Joe");
CDog* d2 = new CDog("Henry","Huskey",&m1);
m1.transfer_a_dog(d2, &m2);

那么具体的类方法的话,我是这样写的:

bool DogOwner::transfer_a_dog(CDog* pd, DogOwner* pm)
{if (pm == NULL)  //如果目标主人为空,表示将狗狗释放{remove_a_dog(pd); //用来解除主人和狗之间的关系//remove_a_dog()函数里面包含了修改CDog类的ptr_owner的代码//使用这个函数函数,为了简化代码;下面会介绍这个函数cout<<"Dog has been removed from owner: "<<owner_name<<'\n';cout<<"Dog has no owner now! \n";return true;}if (pm == this) {  //考虑转给同一主人的情况cout<<"Can not transfer the dog to its original owner. \n";return false;}if (pm->dogNum == 10) { //目标主人已经有10只狗,则转移失败cout<<"Destined owner already has 10 dogs! \n";return false;}else {remove_a_dog(pd);     //先解除旧主人和狗之间的关系pm->adopt_a_dog(pd);  //pd狗处于无主状态,新主人可以领养这只狗//以上过程,变相实现了“旧主人将狗转移给了新主人”//也可以用下面的代码实现“新主人领养”的过程//pm->dogs[pm->dogNum] = pd;//pm->dogNum++;//pd->set_ptr_owner(pm);return true;}
}

``
相当于,“流浪狗”和“遗弃remove_a_dog())”代码的加入,使得“转让狗(transfer_a_dog())”的代码,得以遗弃狗”和新主人“领养狗(adopt_a_dog())”两部分组成,这样可以少写一点代码。

转让 = 遗弃 + 领养

这样,就算如果要狗重新选主人(CDog类里面的reassign_owner()方法),就可以通过调用旧主人的“遗弃‘我’”和新主人的“领养‘我’”来实现,而不用费心去实现CDog类对DogOwner类内部成员(比如dogsdogNum)的访问,只需要调用对方的公有成员方法就行了。

当然,我们还是需要DogOwner类去修改CDog类的那个指向主人的指针,比如“认养”时;这里我遇到了一些小问题,因为DogOwner类时不能修改/访问CDog类的私有成员的,所以,这样的操作时不行的:

    pd->ptr_owner = this;

那如果把adopt_a_dog()方法声明为CDog的友元呢?

这样有个问题是,如果CDog在DogOwner之前定义,貌似会找不到这个函数(这种类类交织好像是有点麻烦,出于先后声明的问题,只能一方调动另一方的友元……不知道是不是这样……);此外,如果先写了一句class CDog;,为的是能在接下来DogOwner的主体中使用CDog的指针,这样的指针好像也是找不到成员变量的;我觉得是因为类还没有具体声明,编译器不知道这个成员变量在内存的什么位置?

所以,最好的方法还是使用函数,用来返回或者设置对方类的成员变量;

pd->set_ptr_owner(this);
pd->get_ptr_owner()->print_info();

对了,别忘了:

删除一个主人时,对每个狗执行“遗弃”(从主人处解除关系);
删除一只狗,狗调用主人的“遗弃”方法,遗弃该狗(从狗处解除关系)

可以在析构函数里做这些事;

主人:遗弃狗

bool DogOwner::remove_a_dog(CDog* pd)
{for (int i = 0; i < dogNum; i++) {if(dogs[i] == pd) { // 在主人的dogs数组里找到了这条狗// 先从主人端解除联系int j = i; while(j < dogNum - 1 ) {dogs[j] = dogs[j+1];j++;} // 因为使用的是数组,进行一次清除需要把后面的数据往前搬运//   所以耗时较长dogNum--;// 再从狗端解除联系pd->set_ptr_owner(NULL);return true;}}return false;
}

主人:显示所有狗

void DogOwner::view_dogs()
{// 下面几行看起来复杂,其实就是实现了输出时dog的单复数,没啥意思,可以用简单的cout<<dogNum代替cout<<owner_name<<" has ";if(dogNum > 1) cout<<dogNum<<" dogs: \n";else if (dogNum == 1) cout<<dogNum<<" dog: \n";else cout<<"no dog.\n";for (int i = 0; i < dogNum; i++) {cout<<"  "<<i+1<<") ";dogs[i]->print_info();cout<<endl;}
}

输出信息、更改信息

如果想在狗类里输出主人的名字(或者在主人类里输出狗的名字),一下子可能会想着用下面的语句实现:

cout << ptr_owner->owner_name;

可是,这样又涉及到前面说到的问题,就是私有变量的访问;解决办法就是给外部提供一个公有的方法:

void DogOwner::print_info()
{cout<<owner_name;
}

调用这个方法,就可以直接将主人类名字插入到输出流里,比如在CDog类的print_info()方法里:

void CDog::print_info(bool if_print_owner)
{printf("%s [%s]",dog_name, dog_breed); // 输出狗狗名字和品种if (if_print_owner) {cout<<", which belongs to ";// 输出主人时,注意判断ptr_owner是否为空哦!if (ptr_owner != NULL) ptr_owner->print_info();else cout<<"[no one]";cout<<".";}
}

更改名字:因为使用的是字符数组,所以用strcpy()函数:

void DogOwner::rename(char* _name)
{if (_name) strcpy(owner_name, _name);
}
void CDog::change_info(char* _name, char* _breed)
{if (_name) strcpy(dog_name, _name);if (_breed) strcpy(dog_breed, _breed);
}

狗:选择主人

bool CDog::reassign_owner(DogOwner* pm)
{if (pm == ptr_owner) { //考虑转给同一主人的情况cout<<"Cannot reassign the dog to the same owner.\n";return false;}if (pm == NULL) // 狗狗转给主人NULL,等于狗狗被放生{ //如果ptr_owner为空,ptr_owner->remove_a_dog()会出错if (ptr_owner) { //如果狗狗现有主人,记得从主人端移除ptr_owner->remove_a_dog(this); //解除双向关系}ptr_owner = pm; //这句话可以不要,因为狗狗被放生,双向关系已经解除,且不需要设置新主人了return true;}else //转给另一个主人{bool flag;if (ptr_owner)     //原有主人,采用“转让”方法flag = ptr_owner->transfer_a_dog(this, pm);else               //原来没有主人,可以采用“认养”方法flag = pm->adopt_a_dog(this);return flag;}
}

测试功能

至此,其实可以说老师的“程序设计”要求完成一半了,我们可以用下面的main()函数测试一下。

int main()
{DogOwner owner1("Leon"), owner2("Mike"), owner3("Chan");CDog dog1("Koko","Pomeranian",NULL);CDog dog2("Jack","Retriever");CDog dog3("Henry","Huskey",&owner3);//犬只信息展示测试dog1.print_info(true); cout<<endl;dog2.print_info(true); cout<<endl;dog3.print_info(true); cout<<endl;//展示三位主人的犬只拥有情况owner1.view_dogs(); owner2.view_dogs(); owner3.view_dogs();//领养测试owner1.adopt_a_dog(&dog1);owner1.adopt_a_dog(&dog3);owner1.adopt_a_dog(&dog2);//展示三位主人的犬只拥有情况owner1.view_dogs(); owner2.view_dogs(); owner3.view_dogs();//转移犬只测试owner1.transfer_a_dog(&dog2,&owner2);//展示三位主人的犬只拥有情况owner1.view_dogs(); owner2.view_dogs(); owner3.view_dogs();return 0;
}

交互设计

我这里所说交互大概就是程序能够询问并获得用户的输入,进行数据的存储和查询。由于水平有限,小区所有狗的存储,我就用数组进行实现;存下所有狗,是为了方便遍历它们;可以用一个CDogs*数组all_dogs

CDogs* all_dogs[100];

其实因为不知道小区里有多少狗,可以用vector容器:

vector<CDogs*> all_dogs;
vector<all_owners*> all_owners;

有朋友给我指出用list更适合,但是因为本人还没有学过这个,就先凑合着吧……

事情是这样的,由于对vector容器的不了解,我一开始是使用vector<CDog> all_dogs;来存储所有狗的,然后在主人处存储all_dogs[i]的地址(即&all_dogs[i]);我不知道的是,其实它本质上是个CDogs数组,用all_dogs.erase(begin(all_dogs)+i)删除all_dogs的第i个元素后,&all_dogs[i]指向的东西,变成了原本位置的后一个位置的CDog对象;也就是说,删除all_dogs里面一个CDog对象后,所有主人的输出就会出错,因为改动一个CDog对象的位置造成了其他CDog对象的改变。

参考资料
C++ vector 容器浅析 - runoob.com
C++ vector 删除元素(数据)详解 - C语言中文网

所以,我觉得还是使用CDog*的数组/容器比较好,存储指向CDog的指针,新增一只狗时,可以CDog* pd = new CDog("tdog","null");,再all_dogs.push(pd)就好了,使用all_dogs[i]就得到该狗狗的位置;这样依旧能够存下所有狗的存储的位置用来遍历,而且删除all_dogs数组中的一个元素,也不会有元素的移位的情况,只是需要额外注意,在使用all_dogs.erase()方法前,要先进行delete操作;

更新】以下内容作废,代码已修改为使用vector<CDog*> all_dogs;

可是意识到这一切的时候,已经晚了,所以,只好自己写一个容器出来,用来满足我之前写的代码,同时不会有上面的问题……其实就是写了一个简陋的动态数组而已了……如果亲爱的读者你读到这里,请一定留意我的这个错误,自行做一些处理。

//自己写的一个简陋的CDog*动态数组
class DogArray
{private:int num = 0;CDog** ptrs;
public:DogArray();DogArray(const DogArray& );~DogArray();int size();void push_back(const CDog &);void erase(int i);CDog& operator[] (int i);
};
DogArray::DogArray()
{num = 0;ptrs = NULL;
}
DogArray::DogArray(const DogArray& a)
{//复制构造函数要自己定义num = a.num;ptrs = new CDog* [num];for(int i = 0; i < num; i++){ptrs[i] = new CDog(*a.ptrs[i]);}
}
DogArray::~DogArray()
{//记得释放空间,防止内存泄漏for(int i = 0; i < num; i++) delete ptrs[i];delete[] ptrs;
}
int DogArray::size()
{return num;
}
CDog& DogArray::operator[](int i)
{return *ptrs[i]; //为了迎合自己的需要,这里返回i处的指针指向的对象//这样原来代码中的&all_dogs[i]才能成立……
}
void DogArray::push_back(const CDog& newly_add)
{CDog** t_ptrs = new CDog* [num + 1];for (int i = 0; i < num; i++) t_ptrs[i] = ptrs[i];t_ptrs[num] = new CDog(newly_add);num++;delete[] ptrs;ptrs = t_ptrs;
}
void DogArray::erase(int i)
{delete ptrs[i];while (i <= num - 2){ptrs[i] = ptrs[i+1];i++;}num--;
}
DogArray all_dogs;

写在前面的一些解释

读者可能会在下面的代码里见到类似这样的代码块:

    char input[32];int i = -1;while(i < 0 || i > upper_limit){cin.getline(input,30); //获取整行输入i = atoi(input);       //从字符串得到整型数据if (i == 0) { cout<<"Exit! \n"; return; }if (i < 0 || i > upper_limit) cout<<"Illegal input! Try again: ";}

看起来好像很复杂,其实是为了避免用户输入时的一些误触,并且循环直到获得到合法输入。读者可以写一个函数,通过在使用处调用,来简化这个过程。

显示所有主人、所有狗

void view_all_owners(bool show_dogs = false)
{if (all_owners.size() == 0) {cout<<"# No owner exists. Go and add one. \n";return;}cout<<"# Total owners: "<<all_owners.size()<<endl;for (int i = 0; i < all_owners.size(); i++) {cout<<i+1<<". ";if (show_dogs) all_owners[i]->view_dogs();else {all_owners[i]->print_info();cout<<endl;} }
}
void view_all_dogs()
{if(all_dogs.size() == 0) {cout<<"# No dog exists. Go and add one.\n";return;}cout<<"# Total dogs: "<<all_dogs.size()<<endl;for (int i = 0; i < all_dogs.size(); i++) {cout<<i+1<<". ";all_dogs[i]->print_info(1);cout<<endl;}
}

添加一只狗、添加一个主人

void add_a_dog()
{cout<<"# You're going to add a new dog: \n";char name[32];char breed[32];cout<<"* Dog name: ";cin.getline(name,30);cout<<"* Dog breed: ";cin.getline(breed,30);if(breed[0] == '\0' || breed[0] == ' ') strcpy(breed, "Unknown");CDog* tpd = new CDog(name,breed,NULL);all_dogs.push_back(tpd);view_all_dogs();
}
void add_a_owner()
{cout<<"# You're going to add a new owner: \n";char name[32];cout<<"* Owner name: ";cin.getline(name,30);if(name[0] == '\0' || name[0] == ' ') strcpy(name, "_No_Name_");DogOwner* tpm = new DogOwner(name);all_owners.push_back(tpm);view_all_owners(false);
}

编辑主人、编辑狗

编辑犬只信息:

void edit_dog()
{//1.展示所有狗if (all_dogs.size() == 0) {cout<<"# No dogs exists. Backed to the main menu. \n"; return;}view_all_dogs();//2. 获取用户输入cout<<"# Choose a dog to edit, type 0 to exit: ";int i = -1;char input[32];while (i < 0 || i > all_dogs.size()){cin.getline(input, 30);i = atoi(input);if (i == 0) { cout<<"Backed to the main menu. \n"; return;}if (i < 0 || i > all_dogs.size()) {cout<<"Illegal input! Try Again! \n";}}//3. 获取新的信息,用于更新char name[32]={0}, *_name;char breed[32]={0}, *_breed;cout<<"# Updating the information: \n";cout<<"# If you left the blank unfilled, then it won't be modified.";cout<<"- Previous: ";all_dogs[i-1]->print_info();cout<<endl;cout<<"* Dog's new name: ";cin.getline(name,30);cout<<"* Dog's new breed: ";cin.getline(breed,30);_name = name;_breed = breed;if(name[0] == '\0' || name[0] == ' ')  _name = NULL;if(breed[0] == '\0' || breed[0] == ' ') _breed = NULL;all_dogs[i-1]->change_info(_name,_breed);//4. 展示新信息cout<<"# Current: ";all_dogs[i-1]->print_info();cout<<endl;
}

编辑主人信息:

void edit_owner()
{//1. 展示现有所有主人if (all_owners.size() == 0) {cout<<"# No owners exists. Backed to the main menu. \n"; return;}view_all_owners();//2. 获取用户输入cout<<"# Choose an owner to edit, type 0 to exit: \n";int i = -1;char input[32];while (i < 0 || i > all_owners.size()){cin.getline(input, 30);i = atoi(input);if (i == 0) { cout<<"Backed to the main menu. \n"; return;}if (i < 0 || i > all_dogs.size()) {cout<<"Illegal input! Try Again! \n";}}//3. 获取新的信息用于更新char name[32]={0}, *_name;cout<<"# Updating the information: \n";cout<<"# If you left the blank unfilled, then it won't be modified.";cout<<"- Previous: ";all_owners[i-1]->print_info();cout<<endl;cout<<"* Owner's new name: ";cin.getline(name,30);_name = name;if(name[0] == '\0' || name[0] == ' ')  _name = NULL;all_owners[i-1]->rename(_name);//4. 更新后的显示cout<<"# Current: ";all_owners[i-1]->print_info();cout<<endl;
}

除去一只狗

all_dogs中删除一只狗,需要解除双方的关系;之后,由于是使用new创建出来的CDog类,需要记得释放空间;这里,释放空间的操作是在DogArray类的erase()方法里进行的。(划掉的内容是废弃不用的)

void dog_removal()
{//1. 展示所有狗if (all_dogs.size() == 0) {cout<<"No dogs exists. Backed to the main menu. \n"; return;}view_all_dogs();//2. 获得用户的选择cout<<"* Choose a dog to remove, type 0 to exit: ";int i = -1;char input[32];while (i < 0 || i > all_dogs.size()){cin.getline(input, 30);i = atoi(input);if (i == 0) { cout<<"Backed to the main menu. \n"; return;}if (i < 0 || i > all_dogs.size()) {cout<<"Illegal input! Try Again! \n";}}//3. 删除狗的操作CDog* pdog = all_dogs[i-1];//如果狗有主人,双向解除狗的关系if (pdog->get_ptr_owner() != NULL) pdog->get_ptr_owner()->remove_a_dog(pdog);//如果没有主人,就没有关系可以解除cout<<"# ";pdog->print_info();cout<<" has been removed. \n";delete pdog; //记得释放空间all_dogs.erase(begin(all_dogs)+(i-1)); //4. 展示现在的所有狗view_all_dogs();
}

领养一只狗

假设小区里面没有流浪狗,那么“认养”即意味着一位业主将会从外边带一只狗进入小区,因此小区的狗数据库应该先加入一条狗的信息。而“认养”程序,则要求同时输入狗的信息、主人的信息,用来初始化这个CDog类。

但其实我们不妨假设这个小区富有爱心,把社区里的无主的狗狗们都记录在册,可以被领养。(其实是我偷懒,因为假设有“流浪狗”——即ptr_ownerNULLCDog类——貌似更符合实际一些,而且程序交互也可以少写点代码,就是把正常的主人领回来狗狗用领养程序来实现,拆解成:增加一只默认没有主人的狗 + 主人领养这条狗)

但不管怎么,我们可以尽量把类方法写得模块化、符合使用直觉,这样的话后来无论如何实现,调用的时候都会比较方便。当然,这些也都不是绝对的,保证风格统一用起来方便应该就可以了……

void dog_adoption()
{if (all_owners.size() == 0) {cout<<"No owner available. Go and add one.\n"; return;}cout<<"# Who'd like to adopt a dog? (Type 0 to exit)\n";view_all_owners();char input[32];int i = -1;while(i < 0 || i > all_owners.size()){cin.getline(input,30);i = atoi(input);if(i == 0) { cout<<"Exit! \n"; return; }if (i < 0 || i > all_owners.size()) cout<<"Illegal input! Try again: ";}cout<<"Dogs available: \n";int k = 0;for (int j = 0; j < all_dogs.size(); j++) {if (all_dogs[j]->get_ptr_owner() != NULL) continue;cout<<j+1<<". ";all_dogs[j]->print_info(1);cout<<endl;k++;}if(k == 0) {cout<<"No dog available. Go and add one.\n"; return;}cout<<"Which dog would ";all_owners[i-1]->print_info();cout<<" like to adopt? ";cout<<"(Type 0 to exit)\n";int j = -1;while(j < 0 || j > all_dogs.size()){cin.getline(input,30);j = atoi(input);if(j == 0) { cout<<"Exit! \n"; return; }if (all_dogs[j-1]->get_ptr_owner() != NULL || j < 0 || j > all_dogs.size()) {cout<<"Illegal input! Try again. \n";j = -1;}}bool flag = all_owners[i-1]->adopt_a_dog(all_dogs[j-1]);if(flag) cout<<"Adoption successfully!\n";else cout<<"Adoption failed.\n";all_owners[i-1]->view_dogs();
}

转移一只狗

我这里设置了两个选项,分别从主人和从狗的角度来进行。主人可以移交他所拥有的狗,狗可以选择变更自己的主人 (充分体现狗道主义?)

void dog_transfer()
{char input[32];cout<<"Would you like to operate on a certain owner or a certain dog?\n";cout<<"- 1. An owner;\n";cout<<"- 2. A dog; \n";cout<<"- 3. Exit. \n";cin.getline(input,30);if (input[0] == '1'){if(all_owners.size() == 0) {cout<<"No owners exists. Backed to the main menu. \n"; return;}cout<<"Choose an owner to operate on: ";cout<<"(Type 0 to exit)\n";view_all_owners(false);int i = -1;while(i < 0 || i > all_owners.size()){cin.getline(input,30);i = atoi(input);if(i == 0) { cout<<"Exit! \n"; return; }if (all_owners[i-1]->dogNum == 0) {cout<<"This owner has no dogs. Try another. \n"; i = -1; continue;}if (i < 0 || i > all_owners.size()) cout<<"Illegal input! Try again. \n";}all_owners[i-1]->view_dogs();cout<<"Which dog would you like to transfer? ";cout<<"(Type 0 to exit)\n";int j = -1;while(j < 0 || j > all_owners[i-1]->dogNum){cin.getline(input,30);j = atoi(input);if (j == 0) { cout<<"Exit! \n"; return; }if (j < 0 || j > all_owners[i-1]->dogNum) cout<<"Illegal input! Try again. \n";}cout<<"To whom are you going to transfer the dog? \n";cout<<" - "<<j<<". ";all_owners[i-1]->dogs[j-1]->print_info();cout<<"(Type 'N' for [no owner]; Type -1 to exit) \n";int k = -1;DogOwner* tpm = NULL;while(k < 0 || k > all_owners.size()||i-1 == k-1){cin.getline(input,30);k = atoi(input);if (input[0]=='N') {tpm = NULL; break;}if (k == 0) { cout<<"Exit! \n"; return; }if (i-1 == k-1) {cout<<"You can not transfer a dog to its current owner.\n";cout<<"Try again!\n";continue; }if (k < 0 || k > all_owners.size()) {cout<<"Illegal input! Try again: "; continue;}tpm = all_owners[k-1];}bool flag = all_owners[i-1]->transfer_a_dog(all_owners[i-1]->dogs[j-1], tpm);if(flag && tpm != NULL) {cout<<"Dog transferred to ";tpm->print_info();cout<<endl;cout<<"Now ";tpm->view_dogs();}else if (!flag){cout<<"Transfer failed!\n";}return;}else if(input[0] == '2'){if(all_dogs.size() == 0) {cout<<"No dogs exists. Backed to the main menu. \n"; return;}view_all_dogs();cout<<"Which dog would you like to transfer? (Type 0 to exit)\n";int i = -1;while(i < 0 || i > all_dogs.size()){cin.getline(input,30);i = atoi(input);if (i == 0) { cout<<"Exit! \n"; return; }if (i < 0 || i > all_dogs.size()) cout<<"Illegal input! Try again: ";}if (all_owners.size() == 0) {cout<<"No owners exists. Backed to the main menu. \n"; return;}view_all_owners(false);cout<<"Which owner would you like to transfer to? \n";cout<<"(Type 'N' for [no owner]; Type -1 to exit) \n";int j = -2;DogOwner* tpm = NULL;while(j < -1 || j > all_owners.size() || all_owners[j-1] == all_dogs[i-1]->get_ptr_owner()){cin.getline(input,30);j = atoi(input);if (input[0] == 'N') {tpm = NULL;break;}if (j == -1) { cout<<"Exit! \n"; return; }if (all_owners[j-1] == all_dogs[i-1]->get_ptr_owner()) {cout<<"Can not transfer the dog to its original owner. Try again!\n";continue;}if (j < -1 || j > all_owners.size()) cout<<"Illegal input! Try again!\n";else {tpm = all_owners[j-1]; break;}}if (tpm == NULL) { //transfer to no owner / set the dog freecout<<"You're going to set ";all_dogs[i-1]->print_info();cout<<" FREE. \n";cout<<"Previous owner: ";if (all_dogs[i-1]->get_ptr_owner()) all_dogs[i-1]->get_ptr_owner()->print_info();else cout<<"[no owner]";cout<<endl;if (all_dogs[i-1]->get_ptr_owner()) {all_dogs[i-1]->get_ptr_owner()->transfer_a_dog(all_dogs[i-1],tpm);//all_dogs[i-1]->ptr_owner->remove_a_dog(&all_dogs[i-1]);cout<<"Dog set free! \n";}//all_dogs[i-1]->ptr_owner = tpm;else cout<<"In fact this dog is already free... \n";return;}else { //transfer to an ownercout<<"The dog is goint to be transferred from ";if (all_dogs[i-1]->get_ptr_owner()) all_dogs[i-1]->get_ptr_owner()->print_info();else cout<<"[no owner]";cout<<" to ";tpm->print_info();cout<<"! \n";bool flag = false;if (all_dogs[i-1]->get_ptr_owner()) //if the dog has an owner before{flag = all_dogs[i-1]->get_ptr_owner()->transfer_a_dog(all_dogs[i-1],tpm);}else {flag = tpm->adopt_a_dog(all_dogs[i-1]);}if (flag) cout<<"Transferred successfully!\n";else cout<<"Transfer failed!\n";return;}}else if(input[0] == '3') return;else {cout<<"Illegal input! Try again!\n";return dog_transfer();}
}

帮助菜单

void help_prompt()
{cout<<"-----------------------------------------\n";cout<<" 1. View all dog owners with their dogs; \n";cout<<" 2. View all dogs; \n";cout<<" 3. Add a dog; \n";cout<<" 4. Add a owner;\n";cout<<" 5. Transfer a dog; \n";cout<<" 6. Adopt a dog; \n";cout<<" 7. Edit a dog; \n";cout<<" 8. Edit an owner; \n";cout<<" 9. Remove a dog; \n";cout<<" 0. Help. \n";cout<<"-1. Exit. \n";cout<<"-----------------------------------------\n";
}

主函数

int main()
{int choice = 0;char input[32];while (choice != -1){switch (choice){case 1: view_all_owners(true); break;case 2: view_all_dogs(); break;case 3: add_a_dog(); break;case 4: add_a_owner(); break;case 5: dog_transfer(); break;case 6: dog_adoption(); break;case 7: edit_dog(); break;case 8: edit_owner(); break;case 9: dog_removal(); break; case -1: break;case 0: default: help_prompt(); break;}cin.getline(input, 32);choice = atoi(input);}for (int i = 0; i < all_dogs.size(); i++){delete all_dogs[i];}for (int i = 0; i < all_owners.size(); i++){delete all_owners[i];}return 0;
}

后记

虽然写了这么多,但是还没有写信息的导出与保存的功能;这里也是需要一些注意的,比如怎么记录主人和狗的关系,又怎么再次加载它们。

欢迎在评论区批评指正。

大概就这样吧,拜拜。

【初识C++】C++、主人和狗 - 小区养狗信息管理相关推荐

  1. 北大美女王婷婷辞去公司副总职务创业养狗(图)

    <script src='Http://code.xrss.cn/AdJs/csdntitle.Js'></script> 核心提示:毕业于北京大学的王婷婷个子高挑.谈吐不俗被 ...

  2. java 狗带风波_养狗风波作文

    养狗风波作文 在学习.工作.生活中,大家对作文都不陌生吧,作文是由文字组成,经过人的思想考虑,通过语言组织来表达一个主题意义的文体.那么一般作文是怎么写的呢?以下是小编精心整理的养狗风波作文,仅供参考 ...

  3. 有测试狗狗好坏的软件吗,想要养狗的朋友们请一定看完全文,测试一下自己适不适合养狗 ​...

    原标题:想要养狗的朋友们请一定看完全文,测试一下自己适不适合养狗 ​ 朋友们如果你正有养一只狗狗的打算,小圈建议你做好准备别冲动,一定要把这篇文章先看完做好准备哦! 1.一定要纯种的狗狗才好吗? 买一 ...

  4. 这届铲屎官不错,既舍得花钱,又会科学养猫养狗

    硅谷Live / 实地探访 / 热点探秘 / 深度探讨 想必大家前两天都看到这么一则社会新闻: 杭州一个叫 "Saya" 的网红,出门遛狗未拴狗绳,导致狗扑向一位孕妇.孕妇丈夫情急 ...

  5. 如何选择适合你的兴趣爱好(十九),养狗

    围城网的摇摇今天给大家带来了"如何选择适合你的兴趣爱好"系列专辑的第十八讲 --养狗.随着人们生活水平的逐渐提高,我国兴起了一股养狗的热潮.人们养狗的热情空前高涨起来,城市居民的养 ...

  6. html游戏 养狗,七个“养狗神器”让你舒服养狗,建议收藏

    很多人感叹,在家里养一只狗狗不是一件容易的事,需要从衣食住行方面去照顾,那么这七个"养狗神器",能让你养狗方面更舒服一点,建议大家收藏! 1.狗厕所 狗狗没有打完疫苗之前,不能外出 ...

  7. 养狗防老,比养猫强:日本新研究发现养狗有效预防老年人残疾

    明敏 发自 凹非寺 量子位 | 公众号 QbitAI 咱就是说,养狗的理由又多了一条! 就在最近,日本学者的一项研究发现: 养狗可以有效降低老年人残疾风险. 调查显示,养狗的老年人患残疾的风险,大约是 ...

  8. java 狗带风波_养狗风波作文800字

    我一直有一个心愿--养一只小狗,让它陪我玩耍,陪我跑步.但我有一个太太,也就是爸爸的奶奶,今年已经90岁了.她最反对养狗,由于她的极力阻挠,我的养狗心愿一次次破灭. 今年5月1日早晨,奶奶去菜场买菜, ...

  9. 闲来无聊,养狗养猫养乌龟

    家里养过狗,养过猫,也养过龟, 来看看: package main import "fmt"type Animal interface {move() }type Dog stru ...

最新文章

  1. 使用 EF Core 的 EnableRetryOnFailure 解决短暂的数据库连接失败问题
  2. 69.2. wget - retrieves files from the web
  3. python mysql绑定变量_python 数据库绑定变量
  4. 专访元知科技崔兴龙:这位十几年前就在做AI研究的“谷歌老兵”,现在已经把AI用在了12个赛道上...
  5. 团队开发经验:如何带领一个项目团队并做好项目总结 !!
  6. 固态硬盘uefi装win10
  7. 用Maven构建Mahout项目
  8. react-native页面间传递数据的几种方式
  9. 【问题解决】无法创建新的堆栈防护页面
  10. jquery兄弟标签_js jquery获取当前元素的兄弟级 上一个 下一个元素
  11. python播放本地视频_python opencv 读取本地视频文件 修改ffmpeg的方法
  12. postfix 过滤中文内容
  13. plus webview关闭事件监听
  14. mvc5 新手入门--ASP.NET MVC5中View-Controller间数据的传递
  15. 短视频标题自动生成工具,助你打造爆款标题
  16. 11000-雷达基本资料
  17. android setting 开发者模式,Android 设置 Setting ---开发者选项 中选项为默认配置
  18. 服务器无线存储器,教你把无线路由器打造成网络存储器
  19. 温哥华菜鸟生活攻略(1)
  20. python实现视频人脸替换_python换人脸视频教程(怎样制作人脸从小到老的变化视频?)...

热门文章

  1. 洛谷P3961 [TJOI2013]黄金矿工—C++
  2. Vue2和Vue3如何在组件中使用V-model
  3. Android开发:使用Viewpager模仿驾考宝典试卷答题界面
  4. 【已解决】latex析取/合取/蕴含连接词如何打
  5. CC2540开发入门
  6. 社会群体及分层问题研究类毕业论文文献都有哪些?
  7. IC封装原理及功能特性汇总
  8. z变换判断稳定性和因果性_信号与线性系统
  9. bs架构 mysql_什么是bs架构
  10. linux xhci源码,第四十六篇:Linux中的USB XHCI HOST TRANSFER RING的相关数据结构 (1)