多态-虚函数
非常感激我队友大爹给我的复习资料
虚函数的作用
允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。
当把基类的某个成员函数声明为虚函数后,允许在其派生类中对该函数重新定义,赋予它新的功能,并且可以通过指向基类的指针指向同一类族中不同类的对象,从而调用其中的同名函数。由虚函数实现的动态多态性就是:同一类族中不同类的对象,对同一函数调用作出不同的响应。
有时在基类中定义的非虚函数会在派生类中被重新定义, 如果用基类指针调用该成员函数, 则系统会调用对象中基类部分的成员函数; 如果用派生类指针调用该成员函数, 则系统会调用派生类对象中的成员函数, 这并不是多态性行为(使用的是不同类型的指针), 没有用到虚函数的功能。
虚函数的使用方法
在基类用virtual声明成员函数为虚函数;这样就可以在派生类中重新定义此函数, 为它赋予新的功能, 并能被方便的调用。在类外定义虚函数时, 不必再加virtual;
在派生类中重新定义此函数, 要求函数名、函数类型、函数参数个数和类型全部与基类的虚函数相同, 并根据派生类的需要重新定义函数体;
定 ...
多态-静态链接和动态链接
非常感激我队友大爹给我的复习资料
多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。
C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
示例
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051#include <iostream>using namespace std;class Shape{ protected: int width; int height; public: Shape(int a=0, int b=0) : width(a), height(b) {} int area(){ cout << "Parent class area: " << endl; ...
多继承-构造函数和初始化
非常感激我队友大爹提供的复习资料
注意:
这里并没有列出全部基类和成员对象, 由于Base3类只有默认构造函数, 不需要给它传递参数,因此, Base3以及Base3类成员对象mem3就不必列出。
其次, 基类名和成员对象名的顺序是随意的。这个派生类构造函数的函数体为空, 可见实际上只是起到了传递参数和调用基类及内嵌对象的作用。
关于执行顺序
先调用基类的构造函数, 再调用内嵌对象的构造函数;
基类构造函数的调用顺序 : 按照派生类定义时的顺序; (2–> 1–> 3)
内嵌对象的调用顺序 : 按照成员在类中声明的顺序; (1–> 2–> 3)
示例
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778#include <bits/stdc++.h>using namespace std;c ...
派生类的构造函数和析构函数
基类的构造函数和析构函数不能被继承,基类成员的初始化只能在基类的构造函数中进行。
当创建一个派生类对象时,派生类的构造函数必须首先通过调用基类的构造函数来对基类的数据成员进行初始化,然后再执行派生类构造函数的函数体,对派生类新增的数据成员进行初始化。当派生类对象的生存期结束时,析构函数的调用顺序相反。
调用构造方式
隐式调用:不指定基类的构造函数,默认调用基类默认构造函数(不带参数或者带默认参数值的构造函数)
显式调用:指定调用基类的某个构造函数。除非基类有默认构造函数,否则都要用显示调用。
简而言之,如果基类有默认构造函数(包括两种情况:1. 不带形参表的构造函数;2. 带形参表,但是形参都有缺省值)的时候,派生类可以隐式调用这些构造函数,其他情况下,必须显式调用,指定基类的构造函数。
示例
隐式调用的情况
1234567891011121314151617181920212223242526272829303132333435363738394041#include <bits/stdc++.h>using namespace std;class A{ ...
多继承-虚继承
非常感激我队友大爹给我的复习资料
为了解决多继承时的命名冲突和冗余数据问题,C++ 提出了虚继承,使得在派生类中只保留一份间接基类的成员。在继承方式前面加上 virtual 关键字就是虚继承。
必须在虚派生的真实需求出现前就已经完成虚派生的操作。在示例中,当定义 D 类时才出现了对虚派生的需求,但是如果 B 类和 C 类不是从 A 类虚派生得到的,那么 D 类还是会保留 A 类的两份成员。换个角度讲,虚派生只影响从指定了虚基类的派生类中进一步派生出来的类,它不会影响派生类本身。
示例
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253/*虚继承(Virtual Inheritance)为了解决多继承时的命名冲突和冗余数据问题,C++ 提出了虚继承,使得在派生类中只保留一份间接基类的成员。在继承方式前面加上 virtual 关键字就是虚继承 1. 必须在虚派生的真实需求出现前就已经完成虚派生的操作。在示例中,当定义 D 类时才出现了对虚 ...
多继承-菱形继承命名冲突
非常感激我队友大爹给我的复习资料
菱形继承
多继承(Multiple Inheritance)是指从多个直接基类中产生派生类的能力,多继承的派生类继承了所有父类的成员。尽管概念上非常简单,但是多个基类的相互交织可能会带来错综复杂的设计问题,命名冲突就是不可回避的一个。
多继承时很容易产生命名冲突,即使我们很小心地将所有类中的成员变量和成员函数都命名为不同的名字,命名冲突依然有可能发生,比如典型的是菱形继承: A->B->D, A->C->D。
在一个派生类中保留间接基类的多份同名成员,虽然可以在不同的成员变量中分别存放不同的数据,但大多数情况下这是多余的:因为保留多份成员变量不仅占用较多的存储空间,还容易产生命名冲突。假如类 A 有一个成员变量 a,那么在类 D 中直接访问 a 就会产生歧义,编译器不知道它究竟来自 A -->B–>D 这条路径,还是来自 A–>C–>D 这条路径。
示例
1234567891011121314151617181920212223242526272829303132333435363738394041 ...
基类派生类
非常感谢我的队友大爹给我的复习资料
面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行效率的效果。
当创建一个类时,您不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。
继承代表了 is a 关系。例如,哺乳动物是动物,狗是哺乳动物,因此,狗是动物,等等。
一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数。定义一个派生类,我们使用一个类派生列表来指定基类。类派生列表以一个或多个基类命名,形式如下:
1class derived-class: access-specifier base-class
其中,访问修饰符 access-specifier 是 public、protected 或 private 其中的一个,base-class 是之前定义过的某个类的名称。如果未使用访问修饰符 access-specifier,则默认为 private。
示例
1234567891011121 ...
运算符重载
非常感激我队友大爹给我的复习资料
您可以重定义或重载大部分 C++ 内置的运算符。这样,您就能使用自定义类型的运算符。
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
1Box operator+(const Box&);
声明加法运算符用于把两个 Box 对象相加,返回最终的 Box 对象。大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。如果我们定义上面的函数为类的非成员函数,那么我们需要为每次操作传递两个参数,如下所示:
1Box operator+(const Box&, const Box&);
示例
1234567891011121314151617181920212223242526272829303132333435363738394041424344#include <iostream>using namespace std;class Box {public: double getV ...
函数重载
重载运算符和重载函数
C++ 允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。
重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。
当您调用一个重载函数或重载运算符时,编译器通过把您所使用的参数类型与定义中的参数类型进行比较,决定选用最合适的定义。选择最合适的重载函数或重载运算符的过程,称为重载决策。
重载函数
在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。您不能仅通过返回类型的不同来重载函数。
下面的实例中,同名函数 print() 被用于输出不同的数据类型:
123456789101112131415161718192021222324252627282930313233#include <iostream>using namespace std;class printData { public: void print(int i) { cout < ...
内嵌对象
非常感激我队友大爹给我的复习资料
类的成员变量为用户自定义的类型时,这些成员变量称为内嵌对象。对象嵌入称做"has-a"关系,就本例来说” World has a Master"。
示例
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051/*类的成员变量为用户自定义的类型时,这些成员变量称为内嵌对象。对象嵌入称做"has-a"关系,就本例来说” World has a Master"。*/#include <iostream>using namespace std;class Master{ private: int _identifier; public: Master(int id) : _identifier(id){ cout << "Master for " ...