本文只是用来熟悉C++的面向对象语法,前提是学过别的
内容概括
- 封装
- 数据都弄成私有的,然后提供共有的对外开放的接口方法,懂的都懂
- 类和对象
- 继承
- 基类 & 派生类(就是父类和子类,叫得这么玄乎)
- 访问控制和继承
- 继承类型
- 多继承
- 多态
- 虚函数
- 纯虚函数
- 接口(抽象类)
- 抽象类的实例
类和对象
函数可在类或结构体中直接定义,也可以只进行声明,在类或结构体外再用命名空间进行定义。
1 | // 在内部定义 |
其他成员函数:
1 | // 普通成员函数 |
派生(继承)
1 | // 这儿有个父类,学生吧,经典例子了 |
这里子类没有构造函数,会调用父类的构造函数,因为继承过来了,包括父类的name字段(被继承为protect字段)。
子类可以有自己的构造函数,但是无论有没有,创建子类对象时,父类构造函数都将被调用。
类访问修饰符:public、private、protect
访问 | public | protected | private |
---|---|---|---|
同一个类 | yes | yes | yes |
派生类 | yes | yes | no |
外部的类 | yes | no | no |
对于子类各种继承的特性:
因此,private里的内容都不能被继承。
现在重新写一下子类,让他有两个重载的子类构造函数:
1 | class PostGraduate : public Student { |
子类不能继承父类的构造函数,只能调用父类构造函数
带参数的子类构造函数:程序会先传入name参数调用父类Student有参构造函数,随后初始化自己的字段,实现了子类对象的初始化。
承上启下:相同函数名执行不同内容
- 重载
- 隐藏
- 覆盖(多态)
多态
隐藏(用得比较少):子类重新定义一个同名函数(参数相不相同都一样),调用子类对象的该父类方法时会出错,即隐藏(而不是重载)。
类似于Java转型时候的问题:
父类对象指针可以指向子类对象,但是只能访问父类中的属性和方法(后面可以使用虚函数实现访问子类方法,实现真正的多态)
子类对象指针指向父类对象时,会报错(因为父类缺少子类的扩展字段或方法)
虚函数
方法前加一个virtual关键字,表示这是一个虚函数(在外部定义是不带virtual的,声明的时候加上就行了)
我们假设有三个类,Student父类,PostGraduate子类,UnderGraduate子类,三个类中都有一个相同名字的虚函数study();
此时创建一个父类指针,当指向不同类的对象时,却可以分别调用他们指向的类自己对应的study()函数。这就实现了多态(相当于子类覆盖了父类方法,但是父类又可以调用子类方法,所以和前面不是用虚函数的隐藏有区别)。
C++多态的核心,就是设置一个父类指针,它可以动态指向不同类的对象,然后动态调用他们自己的方法。
重载是编译时就决定了,多态是运行时动态决定的。
抽象类
假如有一个属于学生类的对象,那么他必然是中小学生、本科生、研究生等中的一员,不可能又是学生又不属于它们中的一种。我们可以将它看作是一种接口、一种标准,而不是一个实例,所以学生类本身的对象是不应该存在的,那么它就叫做抽象类(顾名思义)
设置抽象类依赖于纯虚函数:
1 | //学生类定义 |
只要有一个方法被设置为纯虚函数,Student就自动变为了一个抽象类
此时study()只有声明,没有定义,其具体内容靠子类的多态来实现
现在依然可以创建一个Student的指针,但不能创建Student对象了(因为它是抽象类,还有方法没有被实现,如果能创建这样的对象,调用纯虚函数时你怎么让编译器知道运行啥?)
1 | //这里报错,不允许创建抽象类对象 |
使用了抽象类后,父类Student依然可以有年龄姓名等基本成员,这些成员是所有学生都有的,但父类没有了自己的study方法,因为研究生本科生中小学生每个阶段都有自己的学习内容,所以具体study函数内容靠子类去实现。
这些内容的实现机制依赖于虚表等内容,有时间以后再总结。
参考链接: