面向对象程序设计(多态、转换、继承)
2015-11-06 11:46
766 查看
访问级别
在没有继承之前,类只有两种用户:类本身的成员和该类的用户。将类划分成public和private访问级别,则反映了类本身成员和友元与类用户的分隔。(类成员和友元/类用户)有了继承之后,类的第三用户就是从类派生定义新类的程序员。protected访问,实现允许派生类访问那些仍然不允许类用户访问的成员。
基类提供给派生类型的接口是public和protected组合。
继承
作为基类的类必须是已定义的。包含有纯虚函数的类是抽象基类。不能给抽象基类创建对象。抽象基类的派生类必须 给出所有纯虚函数的定义,否则它们仍为抽象基类。
继承中的类作用域。当存在继承关系时,派生类的作用域嵌套在其基类的作用域之内。(正是因为作用域嵌套的关系,派生类才能像使用自己成员一样使用基类成员)
公有、私有和受保护继承 针对派生类继承来的public和protected成员(private成员不能被派生类访问),在派生列表中使用的访问标号决定该成员在派生类中的访问级别。无论派生列表中是什么访问标号,所有继承于基类的派生类对基类的成员具有与基类相同的访问权限。派生列表中的访问标号是用来控制派生类用户对自基类继承而来的成员的访问权限。
public继承,基类成员在派生类中保持自己的访问级别。
protected继承,基类成员在派生类中都变成protected成员。
private继承,基类所有成员在派生类中都变成private成员。
友元关系不能继承。基类的友元对派生类的成员没有特殊的访问权限。
如果基类定义了static成员,则整个继承层次只有一个这样的成员。
多态
动态绑定关键:对象的实际类型可能不同于该对象引用或指针的静态类型。
静态类型:在编译时可知道的引用类型或指定类型
动态类型:指针或引用所绑定的对象的类型。在运行时可知。
触发动态绑定的两个条件:1、只有指定为虚函数的成员函数才能进行动态绑定,成员函数默认为非虚函数,非虚函数不进行动态绑定。
2、必须通过基类类型的引用或指针进行的函数调用。
引用和指针的静态类型与动态类型可以不同,这个是多态性的基础。
只有通过引用或指针调用,虚函数才在运行时确定。非虚函数总是在编译时根据调用该函数的对象,引用或指针的静态类型而确定。
class Bear:public zooAnimal { public: void rotate(); } class zooAnimal{ public: virtual void rotate(); } Bear b; zooAnimal *pz=&b; Bear *pb=&b; //这里每一个指针都指向Bear object的第一个byte。其间的差距是,pb所 //涵盖的地址包含整个Bear object,而pz所涵盖的地址只包含Bear //object中的zooAnimal suboject(子对象)。
一个对象的引用或指针的静态类型决定了该对象的哪些成员是可见的。
Bear b; zooAnimal za=b; za.rotate(); //这里调用的是zooAnimal::rotate()
za并不是一个Bear,而是一个zooAnimal。多态造成的“一个以上的类型”的潜在力量,并不能够实际发挥在“直接存取object”这件事情上。oo程序设计并不支持object的直接处理。
转换
派生类到基类的转换有两种:派生类对象对基类对象进行赋值或初始化;自动转换引用或指针。赋值或初始化:当一个基类对象直接初始化为一个派生类对象,派生类对象就会被切割以塞入较小的base type内存中,而derived type没有留下任何蛛丝马迹。多态于是不再呈现。基类对象只包含基类中定义的成员,不包含任意派生类型定义的成员。
引用转换时,引用绑定的对象仍然是派生类对象。
只有当派生类是共有继承,才可以使用派生类到基类的转换。
不存在基类到派生类的自动转换
Bear b; zooAnimal *z=&b; Bear *pz=z;//❌ 即使基类指针实际绑定到派生类对象,也不能从基类自动转换到派生类。
构造、复制、析构
构造函数和复制控制成员不能继承。派生类的构造函数受继承影响,需要初始化基类。而基类部分由基类的默认构造函数初始化。派生类只能初始化直接基类。
派生类构造函数的初始化列表,只能初始化派生类的成员,不能直接初始化继承成员。
class obj:public object{ public: obj(string book,size_t qty=0):object(book),min_qty(qty){} }
其他
派生类中的成员函数不会重载基类的成员。一旦派生类成员与基类成员同名,则在该派生类作用域中隐藏该基类成员。因此,基类中的虚函数必须与派生类中对应的虚函数有相同的参数列表,否则将会隐藏基类的虚函数。成员函数无论是否是虚函数都能被重载。派生类可以覆盖基类重载函数的0个/多个实例。如果派生类希望基类所有的重载函数都对它可见,则需要覆盖所有版本或者一个都不覆盖。也可以采用using声明语句,则无需覆盖所有版本。
基类的析构函数要声明为virtual。
zooAnimal *z=new zooAnimal; delete z;//此时应调用zooAnimal的析构函数 z=new Bear; delete z;//此时应调用Bear的析构函数
基类总是需要定义析构函数,并且定义为虚函数。但我们无法推断基类是否需要定义拷贝和赋值操作。(经验规则的例外:如果一个类需要一个析构函数,那么它同样需要拷贝和赋值操作)
派生类的构造、拷贝和赋值不但要操作自己的成员,还要操作基类的成员。而析构函数只负责销毁派生类自己分配的资源(析构顺序:派生类-基类)。
D& D operator=(const D& rhs) { Base::operator=(hrs); ... }
复制控制复制构造函
4000
数、赋值操作运算符和析构函数总称为复制控制。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- PostgreSQL教程(三):表的继承和分区表详解
- Lua编程示例(二):面向对象、metatable对表进行扩展
- C#与.net高级编程 C#的多态介绍
- C#中面向对象编程机制之多态学习笔记
- 浅谈Lua的面向对象特性
- Lua中调用C++函数示例
- Lua面向对象之类和继承浅析
- Lua教程(一):在C++中嵌入Lua脚本
- 浅析Ruby中继承和消息的相关知识
- Lua教程(二):C++和Lua相互传递数据示例
- JavaScript面向对象的两种书写方法以及差别
- 浅谈c# 面向对象之类与对象
- C#中的多态深入理解
- C#面向对象特征的具体实现及作用详解