C++程序员应了解的那些事(7)虚函数的访问控制
<1>注意:基类的指针不能调用派生类自己定义的函数,只能调用虚函数!因为调用普通函数时是绑定静态的,调用虚函数时才动态绑定。
为了支持c++的多态性,才用了动态绑定和静态绑定。理解他们的区别有助于更好的理解多态性,以及在编程的过程中避免犯错误。需要理解四个名词:
对象的静态类型:对象在声明时采用的类型,是在编译期确定的。
对象的动态类型:目前所指对象的类型,是在运行期决定的。对象的动态类型可以更改,但是静态类型无法更改。
静态绑定:绑定的是对象的静态类型,某特性(比如函数)依赖于对象的静态类型,发生在编译期。
动态绑定:绑定的是对象的动态类型,某特性(比如函数)依赖于对象的动态类型,发生在运行期。
//基类的指针不可以调用派生类自身定义的函数(不是虚函数)
#include
using namespace std;
class Base
{
public:
void FuncInBase(){cout << "Func in Base..." << endl;}
};
class Derived:public Base
{
public:
void FuncInDerived() {cout << "Func in Derived..." << endl;}
};
int main(void)
{
Base *p = new Derived();
p->FuncInBase(); //Func in Base
p->FuncInDerived(); //编译不通过
return 0;
}
<2>虚函数的访问控制(编译器根据对象的静态类型来决定访问控制权限,并且进行形参的默认参数的赋值!)
虚函数是实现多态的机制,也就是说在利用基类的指针或者引用调用虚函数时,调用的是该指针或者引用的动态类型的相应的函数,这里有几点需要注意的。
(1) 编译器在决定调用函数时,如果该函数是虚函数才会在运行时确定调用什么函数(动态绑定),如果不是虚函数,那么在编译阶段就已经确定了调用的函数类型(静态绑定)。
如下面的代码,基类与派生类都声明了函数f。但是在main函数的调用中编译器调用的是静态类型对应的函数,因为f函数并不是虚函数,虽然在基类与派生类中都声明了该函数。
class Base
{
public:
void f(int i=0) {cout << "f() in Base..." << i << endl;}
};
class Derived:public Base
{
private:
void f(int i=1){cout << "f() in derived..." << i << endl;}
};
int main(void)
{
Base *b = new Derived();
b->f();
return 0;
}
(2)如下基类定义虚函数为public,派生类覆盖了该虚函数,但是将其声明为private,这样当基类的指针绑定到派生类的对象时,使用该基类指针调用该虚函数时,调用是否成功。如果二者的访问权限反过来呢。
class Base
{
public:
virtual void f(int i=0) {cout << "f() in Base..." << i << endl;}
};
class Derived:public Base
{
private:
void f(int i=1){cout << "f() in derived..." << i << endl;}
};
int main(void)
{
Base *b = new Derived();
b->f();
return 0;
}
//输出为: f() in derived 0
【分析】首先分析为什么输出结果是f() in derived。 编译器在看到b对f进行调用时,此时编译器根据b的静态类型(也就是Base)来决定f函数是否可访问,并且进行形参的默认参数的赋值!!
由于f是虚函数,那么具体调用哪个函数是在运行时确定的,于是在运行时查找Derived的虚函数表,得到虚函数f(此时的f已经被Derived类覆盖,于是调用的就是派生类的版本。)
至于,为什么i的值为0,上述分析也已经说明。
如果将两者的访问权限交换,那么访问控制这一关都过不了,其实很简单,既然你需要派生类继承f函数,将其在Base类中声明为private本身就是不对的。
<3>推荐阅读 https://blog.csdn.net/flymu0808/article/details/39545617
上一篇: 营销文案没新意没法吸引客户 你可能忽略了这些细节!
下一篇: 雪花算法生成id
推荐阅读
-
程序员应了解的那些事(16)C语言中利用setjmp和longjmp做异常处理 / 不要在C++中使用setjmp和longjmp
-
C++程序员应了解的那些事(64)~ 指向 Data Member 的指针 <成员指针>
-
C++程序员应了解的那些事(68)非类型模板参数
-
C++程序员应了解的那些事(47)函数之 传入传出参数 / 默认参数
-
C++程序员应了解的那些事(36)Effective STL第6条:当心C++编译器中最烦人的分析机制 --- 调用构造函数被误认为是函数声明的问题
-
C++程序员应了解的那些事(63)STL内建函数对象、仿函数
-
C++程序员应了解的那些事(62)~ list::splice()函数详解
-
C++程序员应了解的那些事(94)之STL容器内存释放问题
-
C++程序员应了解的那些事(99)之 C++中的ODR法则
-
C++程序员应了解的那些事(18)C++11 通过key访问map容器:下标访问、at()、find、lower_bound&upper_bound、equal_range