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

前言

欢迎收看年度热播大剧《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 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;
}

后记

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

欢迎在评论区批评指正。

大概就这样吧,拜拜。

猜你喜欢

转载自blog.csdn.net/henry_23/article/details/106024285