欢迎收看年度热播大剧《C++、主人和狗》!
【注意】本篇博文仅代表个人思路,希望读者先有自己的思考,避免先入为主。文章内容只是记录C++的初步学习过程,只是一些很简单的C语言内容,并不包含任何高级的数据存储方式或是查找算法;如有大佬光顾,还请多多指正(发抖…)。
欢迎批评指正、讨论交流!
一些链接:
关于标题的抖机灵:点击观看《篱笆·女人和狗》 - 哔哩哔哩
源代码文件(链接不保证有效)20200506.cpp - 超星云盘
对的,这又是一篇做作业的心路历程……话不多说,本周作业原文如下:
以下是教材“14.2 正确处理类的复合关系和继承关系”的部分内容:
https://2d.hep.com.cn/187722/17?from=groupmessage
在此基础上开发一个小区养狗管理程序,功能包括:
- 某主人认养一只狗
- 某主人把自己养的狗转让给别的主人
- 能正确显示某主人养了哪些狗,以及某狗的主人是谁
老师称,布置这个作业的目的,是觉得书上虽然提到了“你中有我,我中有你”的处理办法(没有书的读者,相关内容可以参见作业原文里面的链接,内容是一样的),但是觉得需要进行实践才能更好掌握,所以布置了这样一个作业。
承接书上的内容,狗和主人相互要能找到彼此,这个用指针来实现;沿用书上假设的前提,每个主人最多只能养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
类内部成员(比如dogs
、dogNum
)的访问,只需要调用对方的公有成员方法就行了。
当然,我们还是需要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<
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
来存储所有狗的,然后在主人处存储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*动态数组
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_owner
为NULL
的CDog
类——貌似更符合实际一些,而且程序交互也可以少写点代码,就是把正常的主人领回来狗狗用领养程序来实现,拆解成:增加一只默认没有主人的狗 + 主人领养这条狗)
但不管怎么,我们可以尽量把类方法写得模块化、符合使用直觉,这样的话后来无论如何实现,调用的时候都会比较方便。当然,这些也都不是绝对的,保证风格统一用起来方便应该就可以了……
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 free
cout<<"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 owner
cout<<"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;
}
虽然写了这么多,但是还没有写信息的导出与保存的功能;这里也是需要一些注意的,比如怎么记录主人和狗的关系,又怎么再次加载它们。
欢迎在评论区批评指正。
大概就这样吧,拜拜。