温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

C++中的多态用法总结

发布时间:2020-08-05 05:51:17 来源:网络 阅读:386 作者:玉心软件 栏目:编程语言

1,父类中的方法加virtual与不加virtual的区别

先定义两个beanBaseBeanUserBeanUserBean继承于BaseBeanBaseBean中含有两个属性id(int类型)create_timestd::string类型)。UserBean中含有属性namestd::string类型)。定义两个类BaseModelUserModelUserModel继承于BaseModelBaseModel中定义了一个方法virtual int addRecord(BaseBean *);其定义如下:

int BaseModel::addRecord(BaseBean *data)

{

        std::cout << "base" << std::endl;

        return 0;

}

UserModel中重新声明int addRecord(BaseBean *);并定义这个方法:

int UserModel::addRecord(BaseBean *data)

{

    std::cout << "user" << std::endl;

    return 0;

}

main函数中测试这两个类的addRecord方法:

    BaseModel base;

    UserModel user;

    BaseModel *basePtrOfBase = new BaseModel;

    BaseModel *basePtrOfUser = new UserModel;

    BaseModel &baseRefOfBase = base;

    BaseModel &baseRefOfUser = user;

    BaseModel baseOfUser = user;

    UserBean userBean;

    userBean.setId(1);

    userBean.setName("first user");

    std::cout << "base:";

    base.addRecord(&userBean);

    std::cout << "user:";

    user.addRecord(&userBean);

    std::cout << "basePtrOfBase:";

    basePtrOfBase->addRecord(&userBean);

    std::cout << "basePtrOfUser:";

    basePtrOfUser->addRecord(&userBean);

    std::cout << "baseRefOfBase:";

    baseRefOfBase.addRecord(&userBean);

    std::cout << "baseRefOfUser:";

    baseRefOfUser.addRecord(&userBean);

    std::cout << "baseOfUser:";

baseOfUser.addRecord(&userBean);

运行结果如下:

C++中的多态用法总结C++中的多态用法总结

现在,将BaseModel中的addRecord方法声明语句中的virtual去掉,再次运行结果如下:

C++中的多态用法总结

通过将两次运行结果进行对比,可以看出差异主要在BaseModel *basePtrOfUser = new UserModel;BaseModel &baseRefOfUser = user;定义的实例调用中。所以对于父类中的方法加virtual与不加virtual主要影响的就是使用父类声明的指针或引用对象中。如果使用父类声明的指针或引用对象是使用子类来实例化的。则其调用父类中含有virtual方法时,会调用实例化的子类中的该方法(如果子类中有重写了这个方法的话)

 

2,多态的使用

在使用父类类型作为函数的形参类型时,要注意传递的普通对象和指针及引用的区别。还是以上面的BaseModelUserModel为例。先定义在BaseModel中声明以下几个函数:

    int addRecord(BaseBean );

    virtual int addRecordPrc(BaseBean ) = 0;

virtual int getList(std::list<BaseBean> *rtnList) = 0;

addRecord的定义如下:

int BaseModel::addRecord(BaseBean data)

{

    data.setCreateTime("2019-02-16");

    return this->addRecordPrc(data);

}

UserModel中对addRecordPrcgetList的定义如下:

int UserModel::addRecordPrc(BaseBean data)

{

    UserBean *user = static_cast<UserBean *>(&data);

       std::cout << user->getId() << std::endl;

std::cout << user->getCreateTime().c_str() << std::endl;

return 0;

}

 

int UserModel::getList(std::list<BaseBean> *rtnList)

{

    UserBean user1;

    user1.setId(1);

    user1.setName("user1");

    user1.setCreateTime("2019-02-15");

    rtnList->push_back(user1);

    UserBean user2;

    user2.setId(2);

    user2.setName("user2");

    user2.setCreateTime("2019-02-16");

    rtnList->push_back(user2);

return 0;

}

现在调用UserModel中的addRecord方法。

UserModel userModel;

    UserBean userBean;

    userBean.setId(1);

    userBean.setName("first user");

userModel.addRecord(userBean);

运行结果:

C++中的多态用法总结

现在,在addRecordPrc中加上std::cout << user->getName().c_str() << std::endl;再次运行程序,运行结果如下:

C++中的多态用法总结

可以看到,程序在运行到输出username时崩溃了。这是为什么呢?

现在我们把addRecord的形参类型改为BaseBean *,addRecordPrc的形参不变,调整代码再次运行,发现结果与之前一样。将addRecord的形参类型改为BaseBean,而addRecordPrc的形参类型改为BaseBean *,调整代码再次运行,结果还是与之前一样。将addRecordaddRecordPrc的形参类型都改为BaseBean *,调整代码再次运行程序。程序终于可以正常运行了。运行结果如下:

C++中的多态用法总结

那这到底是为什么呢?为什么使用BaseBean指针就可以,而直接使用BaseBean对象就会导致程序崩溃呢?

addRecord的参数类型是BaseBean时,addRecord的参数变量data只能获取到了设定的idcreate_time两个属性的值,即只是将调用时传递的参数userBean中的属性的值拷贝给了data,且只拷贝了idcreate_time属性,因为data变量没有name属性,所以data变量不能获取到设定的name属性的值。这时,即使它被指针的方式传递给addRecordPrcaddRecordPrc的参数变量获取到的name也是null。而当addRecord的参数类型是BaseBean *时,addRecord的参数变量data获取到的是一个指向包含id,namecreate_time三个属性值的内存的地址,所以,data能够到获取到这三个属性的值。此时如果在addRecord中输出name,是可以成功的。向addRecordPrc的参数传递与此相同。

接下来测试下getList方法:

    std::list<BaseBean> userList;

    userModel.getList(&userList);

    for(std::list<BaseBean>::iterator it = userList.begin(); it != userList.end();it++)

    {

        UserBean *u = static_cast<UserBean *>(&(*it));

        cout << u->getId() << endl;

        cout << u->getCreateTime().c_str() << endl;

        cout << u->getName().c_str() << endl;

}

运行结果:

C++中的多态用法总结

程序崩溃的原因和调用addRecord时相同。调用getList方法时传递的userList中的元素只能获取到从getList方法体中设定的元素的拷贝值。而userList的元素是BaseBean类型,所以只能获取到idcreate_time属性的值。将userList中的元素改为BaseBean *,即将getList的参数类型改为std::list<BaseBean *>*,调整程序,再次运行程序,运行结果为:

C++中的多态用法总结

程序正常。这里要注意,改为std::list<BaseBean *>*后,不能简单的将原来的rtnList->push_back(user1);改为rtnList->push_back(&user1);。这是因为user1getList函数中的局部变量。当getList函数执行完毕后,user1的生命周期就结束了,它的内存空间将会被释放。这时如果再执行上述调用,将会直接导致程序崩溃,而不是像上面那样执行到getName才崩溃。正确的做法是使用new。即:UserBean *user1 = new UserBean;。使用new时,千万不要忘记在调用完后调用delete释放内存空间。如下所示:

    for(std::list<BaseBean *>::iterator it = userList.begin(); it != userList.end();it++)

    {

        UserBean *u = static_cast<UserBean *>((*it));

        cout << u->getId() << endl;

        cout << u->getCreateTime().c_str() << endl;

        cout << u->getName().c_str() << endl;

        delete u;

    }


源代码路径:https://pan.baidu.com/s/1-z5fCUkLTGvaGPXafC__Gg  提取码:b7os

这份源代码是基于Qt Creator写的。如果需要直接编译运行,可以通过Qt Creator直接打开源代码。


向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI