C++中多态性、类成员的访问问题
概要:
C++的多态性是通过关键字virtual指定的, 而Java中,动态绑定是默认的处理方式,java中不需要将方法声明为虚方法。
因此,在C++中要结合类成员的访问控制符、virtual关键字,来分析调用的是哪个成员函数,是否发生多态。
一、规则总结
1、对象只能访问类的public成员,对象只能访问本类声明的public成员和public函数
2、派生类可以访问父类的public、protected成员
3、有继承时,public继承,继承后父类成员的属性不变
protected继承,继承后父类public成员变为protected
private继承,继承后父类public、protected成员变为private
二、程序举列
1、对象只能访问本类声明的public成员和public函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include "iostream" using namespace std; class Base { public : virtual void fun() { print(); } /* void print() //注释掉了,则Base中没有定义print()函数 { printf("Base print\n"); }*/ }; class Derive : public Base { public : void fun() { print(); } void print() { printf("Derive print\n"); } }; int main() { Base b,*pBase; Derive d,*pDeruve; cout<<"-----------------------"<<endl; pBase = &b; pBase->fun(); pBase->print(); //出错 cout<<"--------------------"<<endl; pDeruve = &d; pDeruve->fun(); pDeruve->print(); cout<<"----------------------"<<endl; pBase = &d; pBase->fun(); pBase->print(); //出错 }
该程序会出现如下错误:
error C2065: 'print' : undeclared identifier
error C2039: 'print' : is not a member of 'Base'
原因:因为基类中没有定义print函数,当然就不能访问了。本类的指针只能访问本类中声明的public成员
2、多态性测试
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include "iostream" using namespace std; class Base { public : virtual void fun() { print(); } void print() //virtual { printf("Base print\n"); } }; class Derive : public Base { public : void fun() { print(); } void print() { printf("Derive print\n"); } }; int main() { Base b,*pBase; Derive d,*pDeruve; cout<<"-----------------------"<<endl; pBase = &b; pBase->fun(); pBase->print(); cout<<"--------------------"<<endl; pDeruve = &d; pDeruve->fun(); pDeruve->print(); cout<<"----------------------"<<endl; pBase = &d; pBase->fun(); pBase->print(); }
该程序中,基类定义了fun函数和print函数,其中fun函数是虚函数。当让基类的指针指向派生类对象时,通过基类指针调用fun函数时,实际上调用的就是派生类的fun函数(多态性)。而当用基类指针调用print函数时,由于基类中定义了print函数,所以可以访问。又由于print函数不是虚函数,所以调用的是基类的print函数。
输出结果为:
3、基类成员函数调用virtual函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include "iostream" using namespace std; class Base { public : void fun() { print(); } virtual void print() // virtual { printf("Base print\n"); } }; class Derive : public Base { public : void fun() { print(); } void print() { printf("Derive print\n"); } }; int main() { Base b,*pBase; Derive d,*pDeruve; cout<<"-----------------------"<<endl; pBase = &b; pBase->fun(); pBase->print(); cout<<"--------------------"<<endl; pDeruve = &d; pDeruve->fun(); pDeruve->print(); cout<<"----------------------"<<endl; pBase = &d; pBase->fun(); pBase->print(); }
print是虚函数,pBase->print()由于多态性,调用派生类的成员函数。在pBase->fun()中,由于fun是基类的public函数,且不是虚函数,所以直接调用基类的fun函数,在fun函数中,它调用了基类的print函数,这类似于pBase->print(),由于多态性也调用了派生类的print函数;
输出结果:
注意:如果是基类的构造函数中,调用了print函数(此时print为虚函数),即
Base()
{
printf("Base 构造函数 ");
print();
}
当定义一个派生类对象时,基类的构造函数会先调用,此时基类构造函数中调用的是基类的print函数本身,不会发生多态。
书《Effective C++ third edition》中解释如下:
在derived class对象的base构造期间,对象的类型是base class,而不是derived class,此时基类的virtual 函数可看做不是虚函数
(完)