1、继承机制的两种特性:多态和动态绑定
2、当我们在C++说到多态性时,我们主要指基类的指针或引用可以指向其任意派生类的能力。也就是说,只有在通过基类指针或引用间接指向派生类子类型时,多态性才会起作用。一个对象的多态性操纵行为要求“通过指针或引用来访问该对象”,但是,在C++中,指针或引用的操作本身不一定导致多态性。例如:
//没有多态性
int *pi ;
//没有语言支持的多态性
void *pvi ;
//可以:pquery可以指向任何Query派生类
Queue *pquery ;
3、在C++中,多态性只存在于类继承层次中。void*型的指针可以被描述为多态,但是语言本身并没有显式地支持他们。
4、派生类可以直接访问基类的非私有成员,不能直接访问基类中的私有成员。那么如果派生类需要访问基类的私有成员,该怎么办呢?方法有:(1)如果派生类希望直接的访问其基类的私有成员,则该基类必须显示地把派生类声明为基类的一个友元。(2)、在基类中定义某个公共成员函数访问该私有成员。例如string GetName()。这样派生类可以继承该函数,从而可以访问该私有成员。(3)、将该私有成员的访问级别改为protected。这样派生类就可以直接访问它了。这也是惯用的方法,即把基类的数据成员声明为protected。
5、在派生表中指定的类必须首先被定义好,方可被指定为基类。例如:
//错误:Query必须已经被定义好
class Query ;
class NameQuery : public Query { ... };
Query的前向声明不足以使其被用作基类,因为没有被定义好。派生类的前向声明不能包括它的派生表,而只是类名——与非派生类一样。例如:
//错误:前向声明不能包含派生类的派生表
class NameQuery : public Query ;
正确的前向声明如下:
//派生类与非派生类的前向声明只列出类名
class Query ;
class NameQuery ;
6、在C++中,基类指针只能访问在该类中被声明(或继承)的数据成员和成员函数,包括虚拟成员函数,而与它可能指向的实际对象无关。例如:
//Query是基类,NameQuery是Query类的派生类
Query *pb = new NameQuery("lai") ;//将一个派生类NameQuery对象的地址初始化一个Query基类指针
//如果我们调用在Query基类中定义的虚拟函数,如:
pb->eval() ; //调用NameQuery::eval()
//如果我们想通过pb直接访问NameQuery的成员,则会出错
pb->_name ;
从上面的例子分析,我们可以总结一条:除了“在Query基类中被声明、并且在NameQuery派生类中被改写”的虚拟函数之外,我们没有办法通过pb直接访问NameQuery的成员:
(1)、如果Query和NameQuery都声明了一个同名的非虚拟成员函数,则通过pb调用的总是Query的实例
(2)、类似的,如果Query和NameQuery都声明了一个同名的数据成员,则通过pb调用总是访问Query的实例
(3)、如果NameQuery引入了一个在Query中不存在的虚拟函数,那么,试图通过pb调用它就会导致一个编译时刻的错误:
//错误:suffix()不是Query的成员
pb->suffix() ;
(4)、类似的,如果我们试图通过pb访问NameQuery的数据成员或非虚拟成员函数,也会产生一个编译时刻的错误:
//错误:_name不是Query的成员
pb->_name ;
//在这种情况下,即使对要访问的成员进行限定修饰,也不起作用
//错误:Query没有NameQuery基类,即NameQuery不是Query的基类
pb->NameQuery::name() ;
上面的四条,我们总结下:一个指针的类型的是基类的话,它只能访问在基类中被声明、并且在派生类中被改写的虚拟函数,不能通过该指针之间访问派生类中的独有数据成员和非虚拟成员函数。还有就是,如果基类和派生类中出现了同名的数据成员或同名的非虚拟成员函数,则通过该指针访问这个数据成员或非虚拟成员函数时,总是调用的是基类中的数据成员实例或非虚拟成员函数的实例。因为该指针的类型是基类类型。