C++中的多态用法总结-创新互联

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

成都服务器托管,创新互联公司提供包括服务器租用、服务器托管、带宽租用、云主机、机柜租用、主机租用托管、CDN网站加速、域名与空间等业务的一体化完整服务。电话咨询:028-86922220

先定义两个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直接打开源代码。

另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。


网站标题:C++中的多态用法总结-创新互联
分享URL:http://cdiso.cn/article/dpicgp.html

其他资讯