首先,
基类的指针可以指向继承类的Object 而无需 explict type cast,
基类的引用(reference) 可以引用继承类的Object 而无需 explict type cast,
但是,一个基类的 pointer 或者 reference 能,且仅能 调用基类的方法,也就是说一个基类的 pointer 或者 reference不能调用继承类的方法。
其次,
你不能将基类的 Objects 或 地址 赋于 继承类的 references 或 pointers
比如 A 为基类, B 为继承类
A a;
B & rb = a; // not allowed
B * pb = &a; // not allowed
从上面的描述你看到了
基类的 reference 和 pointers 可以指向继承类的objects, 也就是说
B b;
A & ra = b; // is allowed
A * pa = &b; // is allowed
这样就引出了下面的导论:
函数的参数如以基类的reference 或 pointer 作为参数,那么该参数可以访问基类的方法,也可以访问继承类的方法,由这一点又引出一个问题,如果在基类和继承类中有两个相同名字的函数,比如 在 A 中定义了方法
void func(){} 在 B中也定义了方法 void func(){// some code}
现在有一个函数:
void show(const A & ra)
{
ra.func();
}
那么这个func() 到底是基类中的func() 呢?还是继承类中的func() 呢?
这其实就是引出了我们为什么要引进 virtual 这个概念.
没有virtual 这个概念,我们上面这个func() 将是基类中的func(); 它取决于那个参数类型,也就是说,在参数中,那个类型为A,而A是基类,所以 ra 调用的是 基类中的那个 func().
而一旦我们引入了virtual 这个概念,那么这个判断就是另外个判断法了,比如:
class A
{
public:
virtual void func(){}
virtual ~A(){}
};
class B : public A
{
public:
void func(){ cout<<"haha";}
};
在main 中的代码如:
Aa;
B b;
A & a1_ref = a;
A & a2_ref = b;
a1_ref.func(); // 调用的是 A 的 func()
a2_ref.func(); // 调用的是 B 的 func()
也就是到底调用的是基类中函数还继承类中的函数,取决于所引用或所指向的对象.
这里我们看出来了,为什么C++ 引入了 virtual 这个概念.
再回到你的问题上来,诚如你所说的,假设C++ 没有 virtual 这个概念,那么当在继承类(子类)中的函数覆盖了基类(父类)中的函数时,继承类的对象将只能调用其该类中定义的函数.如果没有覆盖,将调用父类中的函数.
而有了virtual 以后,将使函数调用增加了灵活性,使本来做不到的事情成为了可能.这就是 virtual 带来的用处或好处.
现在观察下面的代码:
#include<iostream>
#include<cstdlib>
using namespace std;
class Father
{
public:
virtual void func(){cout<<"invoke base class function"<<endl;}
virtual ~Father(){}
};
class Son : public Father
{
public:
void func(){cout<<"invoke derived-class function"<<endl;}
};
int main()
{
Father father;
Son son;
Father & f1_ref = father;
Father & f2_ref = son;
f1_ref.func();
f2_ref.func();
son.func();
system("pause");
return 0;
}
我们来比较 f2_ref.func(); 和 son.func(); 它们之间有区别吗?回答是:它们之间没有区别,f2_ref 只是son的一个引用,也就是说,f2_ref 只是son的一个别名,也就是说,它们是同一个Object ,所以 f2_ref.func(); 和 son.func();它们之间是没有区别的.但是你看到了,由于存在了virtual我们通过父类的Object对子类的引用,从而调用子类的函数成为了可能,这样便给语言增加了灵活性,这也就是所谓的好处吧.
所以我们通常将函数定义成下面的形式:
ReturnType FunctionName(ParameterType & name); // Caution: 这里的ParameterType 为父类的类型,这样便使得该参数既可调用父类中的函数,又可调用子类中的函数.