C++中的多态用法总结

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

目前成都创新互联已为数千家的企业提供了网站建设、域名、网页空间、绵阳服务器托管、企业网站设计、焉耆网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。

先定义两个bean。BaseBean和UserBean。UserBean继承于BaseBean。BaseBean中含有两个属性id(int类型)和create_time(std::string类型)。UserBean中含有属性name(std::string类型)。定义两个类BaseModel和UserModel。UserModel继承于BaseModel。BaseModel中定义了一个方法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,多态的使用

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

    int addRecord(BaseBean );

    virtual int addRecordPrc(BaseBean ) = 0;

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

addRecord的定义如下:

int BaseModel::addRecord(BaseBean data)

{

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

    return this->addRecordPrc(data);

}

在UserModel中对addRecordPrc和getList的定义如下:

int UserModel::addRecordPrc(BaseBean data)

{

    UserBean *user = static_cast(&data);

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

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

return 0;

}

 

int UserModel::getList(std::list *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++中的多态用法总结

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

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

C++中的多态用法总结

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

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

接下来测试下getList方法:

    std::list userList;

    userModel.getList(&userList);

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

    {

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

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

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

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

}

运行结果:

C++中的多态用法总结

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

C++中的多态用法总结

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

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

    {

        UserBean *u = static_cast((*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直接打开源代码。


分享名称:C++中的多态用法总结
文章转载:http://scyanting.com/article/iecjco.html