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);
运行结果如下:
现在,将BaseModel中的addRecord方法声明语句中的virtual去掉,再次运行结果如下:
通过将两次运行结果进行对比,可以看出差异主要在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
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
std::cout << user->getId() << std::endl;
std::cout << user->getCreateTime().c_str() << std::endl;
return 0;
}
int UserModel::getList(std::list
{
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);
运行结果:
现在,在addRecordPrc中加上std::cout << user->getName().c_str() << std::endl;再次运行程序,运行结果如下:
可以看到,程序在运行到输出user的name时崩溃了。这是为什么呢?
现在我们把addRecord的形参类型改为BaseBean *,addRecordPrc的形参不变,调整代码再次运行,发现结果与之前一样。将addRecord的形参类型改为BaseBean,而addRecordPrc的形参类型改为BaseBean *,调整代码再次运行,结果还是与之前一样。将addRecord和addRecordPrc的形参类型都改为BaseBean *,调整代码再次运行程序。程序终于可以正常运行了。运行结果如下:
那这到底是为什么呢?为什么使用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
userModel.getList(&userList);
for(std::list
{
UserBean *u = static_cast
cout << u->getId() << endl;
cout << u->getCreateTime().c_str() << endl;
cout << u->getName().c_str() << endl;
}
运行结果:
程序崩溃的原因和调用addRecord时相同。调用getList方法时传递的userList中的元素只能获取到从getList方法体中设定的元素的拷贝值。而userList的元素是BaseBean类型,所以只能获取到id和create_time属性的值。将userList中的元素改为BaseBean *,即将getList的参数类型改为std::list
程序正常。这里要注意,改为std::list
for(std::list
{
UserBean *u = static_cast
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://scpingwu.com/article/iecjco.html