1、继承,动态绑定,数据抽象一起成为面向对象编程的基础。
2、模板使我们能够编写独立于具体类型的泛型类和泛型函数。在C++中,用类进行数据抽象,用类派生从一个类继承另一个类:派生类继承基类的成员。动态绑定使编译器能够在运行时决定是使用基类中定义的函数还是类中定义的函数。
3、C++中,多态性仅用于通过继承而相关联的类型的引用或指针。
4、定义为virtual的函数是基类期待派生类重新定义的。保留字virtual只在类内部的成员函数声明中出现。
5、一旦函数在基类中声明为虚函数,它就一直为虚函数。
6、派生类对象由多个部分组成:派生类本身定义的(非static)成员加上由基类(非static)成员组成的子对象。
7、已定义的类才可以作为基类。
8、如果需要声明(但并不实现)一个派生类,则先声明包含类名但不包含派生列表。
示例
//error: a forward declaration must not include the derivation list class Bulk_item : public Item_base; //正确的前向声明为: // forward declarations of both derived and nonderived class class Bulk_item; class Item_base;
9、要触发动态绑定,要满足两个条件:(1)只有指定为虚成员函数才能动态绑定;(2)必须通过基类类型的引用或指针进行函数调用。
基类类型的引用或指针可以引用基类类型对象,也可以引用派生类型对象。
10、基类类型引用和指针的关键点在于静态类型(在编译时可知的引用类型或指针类型)和动态类型(指针或引用所绑定的对象的类型这是仅在运行时可知的)可能不同。
引用和指针的静态类型与动态类型可以不同,这是C++可以支持多态性的基石。
11、覆盖虚函数机制
有时,希望覆盖虚函数机制并强制函数调用使用虚函数的特定版本,可以使用作用域操作符:
示例
Item_base *baseP = &derived; double d = baseP->Item_base::net_price(42); //调用基类的版本
12、派生类虚函数调用基类版本时,必须显式使用作用域操作符。如果派生类函数忽略了这样做,则函数调用会在运行时确定并且将是一个自身调用,从而导致无穷递归。(指的是在派生类虚函数中调用基类的同名的函数,从而让基类做一些必要的工作)
13、像其他任何函数一样,虚函数也可以有默认实参。通常,如果有用在给定调用中的默认实参值,该值将在编译时确定。如果一个调用省略了具有默认值的实参,则所用的值由调用该函数的类型定义,与对象的动态类型无关。通过基类的引用或指针调用虚函数时,默认实参为在基类虚函数声明中指定的值,如果通过派生类的指针或引用调用虚函数,则默认实参是在派生类的版本中声明的值。
在同一虚函数的基类版本和派生类版本中使用不同的默认实参几乎一定会引起麻烦。如果通过基类的引用或指针调用虚函数,但实际执行的是派生类中定义的版本,这时就可能会出现问题。在这种情况下,为虚函数的基类版本定义的默认实参将传给派生类定义的版本,而派生类版本是用不同的默认实参定义的。
14、如果成员在基类中为private,则只有基类和基类的友元可以访问该成员。
15、接口继承和实现继承
public派生类继承基类的的接口,它具有与基类相同的接口,称为接口继承。
private和protected派生的类不继承基类的接口,称为实现继承。派生类的实现中使用被继承类但继承基类的部分并未成为其接口的一部分。
16、继承:反映的是is a的关系。组合反映的是has a的关系。
17、可以通过using声明访问基类中的名字(使用private继承才有这个必要)。
示例
class Derived:private base { public: using Base::SIZE; //... };
18、使用class保留字定义的派生类默认具有private继承,而用struct保留字定义的类默认具有public继承。这是他们唯一的区别:默认的成员保护级别和默认的派生保护级别。
19、友元关系和继承
友元关系不能继承。基类的友元对从该基类派生的类没有特殊访问权限。
在http://www.cnblogs.com/mydomain/archive/2011/03/22/1991244.html中,我经过VS2008编译环境测试得出友元关系可以继承。而C++ Primer 4th中第15章讲到这个情况时说不能继承。对其例子进行了测试,发现也是可以通过编译的。
应当是编译环境的问题。
示例
#include "iostream" #include "vector" #include "algorithm" #include "string" #include "functional" using namespace std; class Base { friend class Frnd; protected: int i; }; class D1:public Base { protected: int j; }; class Frnd { public: int mem(Base d) {return d.i;} int mem(D1 d1) {return d1.i;} //error:friendship dosenot inherit }; class D2:public Frnd { public: int mem(Base d) {return d.i;};//“Base::i”: 无法访问 protected 成员,在编译中出错 }; int main() { return 0; }
20、继承和静态成员
如果基类定义 static 成员,则整个继承层次中只有一个这样的成员。无论从基类派生出多少个派生类,每个 static 成员只有一个实例。static 成员遵循常规访问控制:如果成员在基类中为 private,则派生类不能访问它。假定可以访问成员,则既可以通过基类访问 static 成员,也可以通过派生类访问 static 成员。一般而言,既可以使用作用域操作符也可以使用点或箭头成员访问操作符。
参考
[1] http://blog.163.com/zhoumhan_0351/blog/static/3995422720100284731826/