Note06

来源:互联网 发布:linux系统的手机 编辑:程序博客网 时间:2024/05/22 13:18

书:C++大学教程(第七版)
1. 在派生类的构造函数中,采用成员初始化器列表显式地调用初始化成员对象和调用基类的构造函数,可以防止重复初始化,即调用了默认构造函数之后,又在派生类构造函数中再次修改数据成员。
2. public、protected、private继承
(1) protected继承,基类的public和protected成员都变成派生类中的protected成员。
(2) private继承,基类的public和protected成员都变成派生类的private成员。
private和protected继承不是“是一个”关系。
3. “is a”和“has a”
(1) “是一个”表示继承。在“是一个”关系中,派生类的对象都可以看成是其基类的一个对象;
(2) “有一个”表示组成。在“有一个”关系中,一个对象可以吧其他类的一个或多个对象拿来作为自己的成员,但是在它的接口中并不直接暴露这些成员对象的行为;
4. 基类(父类)和派生类(子类)
利用基类指针,编译器只允许调用基类的成员函数。因此,如果基类指针指向了派生类对象,并且试图访问只在派生类中拥有的成员函数,那么就会产生编译错误。不过,如果基类使用向下强制类型转换技术(downcasting),基类可以访问只在派生类中拥有的成员。
5. 一旦一个函数声明为virtual,那么从整个继承层次的那一点起向下的所有类中,它将保持是virtual的,即使当派生类重写此函数时并没有显示地将它声明为virtual。
如果程序通过指向派生类对象的基类指针(比如,shapePtr->draw())或者指向类派生类对象的基类引用(比如,shapeRef.draw())调用virtual函数,那么程序会根据所指向对象的类型而不是指针类型,动态(即执行时)选择正确的派生类draw函数。在执行时(不是编译时)选择合适的调用函数成为动态绑定。
当virtual函数通过按名引用特定对象和使用圆点成员选择运算符的方式(如,squareObject.draw())被调用时,调用哪个函数在编译时就已经决定了(称为静态绑定),所调用的virtual函数正是为该特定对象所属的类定义的(或继承而来的)函数,这并不是多态性行为。因此,使用virtual函数进行动态绑定只能通过使用指针或引用句柄完成。
6. 通过声明类的一个或多个virtual函数为纯virtual函数,可以使一个类成为抽象类。一个纯virtual函数是在声明时“初始化为0”的函数,如下所示:
virtual void draw() const = 0;
“=0”成为纯指示符(pure specifier)。纯virtual函数不提供函数的具体实现,每个派生的具体类必须重写所有基类的纯virtual函数的定义,提供这些函数的具体实现。
virtual函数和纯virtual函数之间的区别是:virtual函数有函数的实现,并且提供派生类是否重写这些函数的额选择权。相反,纯virtual函数并不提供函数的实现,需要派生类重写这些函数以使派生类成为具体类,否则派生类仍然是抽象类。
尽管不能实例化一个抽象基类的对象,但是可以利用抽象基类声明指针和引用,使他们可以引用任何从抽象类派生的具体类的对象。程序通常利用这种指针和引用国泰地操作派生类的对象。
7. 多态性、virtual函数、动态绑定的底层实现机制
(1) 多态性是通过包含了三级指针的一种精致的数据结构实现的。
(2) 第一级指针:virtual函数表中的函数指针。当调用virtual函数时,这些指针指向实际执行的函数。
(3) 第二级指针:无论何时当实例化具有一个或多个virtual函数的类的对象时,编译器给这个对象附上一个指针,指向对象所属类的virtual函数表。这个指针通常放在对象的前部,但不做特别要求。
(4) 第三级指针仅仅包含接受virtual函数调用的对象句柄。这个级别中的句柄也可以是引用。
(5) 对于大多数应用来说,多态性的开销是可以接受的。但在某些情况下(比如性能要求很高的实时应用程序),多态性的开销可能就太高了。
8. 虚析构函数
(1) 如果要删除一个具有非虚析构函数的派生类对象,却显示地通过指向该对象的一个基类指针,对它应用delete运算符,那么C++标准会指出这一行为未定义。解决办法:在基类中创建virtual析构函数。
(2) 如果一个类含有virtual函数,该类就要提供一个virtual析构函数,即使该析构函数并不一定是该类需要的。这可以保证当一个派生类的对象通过基类指针删除时,这个自定义的派生类析构函数(如果存在的话)会被调用。
(3) 构造函数不能是virtual函数。
9. 函数模板

   template<typename T>
 或template< class ElementType>或template< typename BorderType, typename FillType>class或者typename指出函数模板形参,实际上就是指“任何内置类型或用户定义类型”。

10. 重载函数模板
(1) 一个函数模板同样可以被一个具有相同函数名但有不同函数参数的非模板函数所重载。
(2) 编译器在函数调用时进行匹配处理来决定调用哪一个函数。首先,编译器会找出所有和调用函数的函数名想匹配的函数模板,然后根据调用函数的参数类型对函数模板进行特化。接着编译器找出所有与调用函数里的普通函数想匹配的函数,如果某一个普通函数或者函数模板特化是与调用函数最匹配的,那么这个普通函数或者函数模板特化将被调用。如果一个普通函数和一个模板特化跟调用函数的而匹配程度相同,那么这个普通函数将被调用。否则,如果有多个函数和调用函数相匹配,编译器会认为调用有二义性,将产生一个错误信息。
(3) 类模板的成员函数的定义就是函数模板。出现在类模板定义之外的成员函数定义的开头都是template< typename T >;因此,每一个模板函数的定义都想定义普通函数一样。
(4) 类模板的非类型参数:template< typename T, int elements >;
(5) 类型参数可以指定其默认类型,如:template< typename T = string>;默认为string类型;声明时为:Stack<> jobDescriptions;
(6) 性能提示:在编译的时候适当地(可以利用一个非类型模板参数)指定一个容器类的大小(比如一个数组类或者是一个堆栈类),这样做消除了使用new创建动态空间的执行时间开销。
11. 模板和继承的注意事项
类模板特化又称为类模板具体化,可理解为“类的实例化”
(1) 类模板可以从类模板特化派生得到;
(2) 类模板可以从非类模板类派生得到;
(3) 类模板特化可以从类模板特化派生得到;
(4) 非模板类可以从类模板特化派生得到;

0 0
原创粉丝点击