1、虚函数
1.1、虚函数的作用是什么?
- 虚函数并不是不被实现的函数。定义虚函数可以使一个基类的指针可以调用子类的这个函数。
- 纯虚函数才是不被实现的函数。
- 定义纯虚函数使是为了标准化一个接口,让规范集成这个类的程序员必须实现这个类。
对于虚函数的理解,可以认为这是一种“动态编译”机制,在编译时并不确定调用的是哪一个函数,而是在运行时确定。由于编译时不能确定函数是基类的函数还是派生类的函数,就称为虚函数。
1.2、纯虚函数的定义方法:
纯虚函数是在基类中声明的函数,它在基类中仅作声明,任何派生类都需要定义自己的实现方法。
virtual ReturnType Function()= 0;
例如:virtual void funtion1()=0;
引入纯虚函数的原因
- 方便使用多态特性
- 很多时候基类生成对象本省并不合理,而是用基类的派生类来实现一个具体的对象。将函数定义为纯虚函数之后,就必须要求派生类实现这个函数,从而实现多态特性。而含有纯虚函数的类为抽象类,它不能直接生成对象.
引入纯虚函数,可以使派生类继承基类的函数接口。
具有纯虚函数的类为抽象类。
1.3 抽象类的作用
抽象类的主要作用是将有关的操作作为结果接口组织在一个继承层次结构中,由它来为派生类提供一个公共的根,派生类将具体实现在其基类中作为接口的操作。所以派生类实际上刻画了一组子类的操作接口的通用语义,这些语义也传给子类,子类可以具体实现这些语义,也可以再将这些语义传给自己的子类。
使用抽象类时要注意的点有:
- 抽象类只能作基类,若其派生类没有重新定义纯虚函数,那么这个派生类仍然是一个抽象类。若子类给出了纯虚函数的实现,那么子类就是一个可以建立对象的具体的类
- 抽象类不能定义对象
1.4、 虚函数
- 虚函数声明如下:virtual ReturnType FunctionName(Parameter);虚函数必须实现,如果不实现,编译器将报错。
- 对于虚函数来说,父类和子类都有各自的版本。由多态方式调用的时候动态绑定
- 若子类实现了纯虚函数,那么这个函数就变成虚函数,子类的子类还可以重新实现这个虚函数,在调用的使用进行动态绑定。
1.5、
友元不是成员函数,而只有成员函数才能被定义为虚函数
1.6、指向抽象类的指针
我们说过,一个抽象类不能具体化一个对象,但是可以生成它的指针。
1.7、一些概念
- 依赖:一个类是另一个类的参数或者返回值
- 关联:一个类是另外一个类的成员变量
- 组合:代表整体的对象、代表部分对象的生命周期
- 聚合关系是更强的关联关系。聚合是整体是部分之间的关系,如汽车和发动机,汽车可以选择不同品牌的发动机
1.8c++是如何实现多态的?
c++多态分为静态多态和动态多态。
- 静态多态是函数重载;是在编译器在编译时就决定函数地址。
- c++编译器为每一个含有虚函数的子类和父类建立虚函数表和vptr指针,然后根据vptr指针找到这个虚函数表。也就是运行时根据虚函数表找到函数地址。
关于动态多态的补充:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是子类,就调用子类的函数;如果对象类型是父类,就调用父类的函数,(即指向父类调父类,指向子类调子类)此为多态的表现。
问?在父类的构造函数里调用多态函数,能够产生多态?
答:不能实现多态。
这个问题涉及到vptr指针的初始化问题。在构造子类对象时,如果执行父类的构造函数,那么vptr就会指向父类的虚函数表,这时调用虚函数就会调用父类的虚函数。
那么,对象的vptr是什么时候构造的呢?
在对象创建时由编译器对vptr进行初始化;当对象的构造完全结束后VPTR的指向才最终确定。
父类对象的VPTR指向父类虚函数表;子类对象的VPTR指向子类虚函数表。
简言之,虚函数是c++中实现动态多态的机制,让基类指针能够仿真派生类定义的函数。
2、关于类的对象与类的指针的区别?
1、现在定义一个class:
class Student{
......
}
student *s1 = new student; // 通过指针
student s2; // 直接定义类对象
类的指针:他是一个内存地址值,他指向内存中存放的类对象(包括一些成员变量所赋的值).
对象:他是利用类的构造函数在内存中分配一块内存(包括一些成员变量所赋的值).
2、差别
1.引用成员: 对象用" . "操作符; 指针用" -> "操作符.
2.生命期: 若是成员变量,则是类的析构函数来释放空间;若是函数中的临时变量,则作用域是该函数体内;而指针,则需利用delete 在相应的地方释放分配的内存块.
注意:用new ,一定要delete..
类的对象:用的是内存栈,是个局部的临时变量.
类的指针:用的是内存堆,是个永久变量,除非你释放它.
3、使用虚函数时的差别
当类是有虚函数的基类,Func是它的一个虚函数,则调用Func时:
类的对象:调用的是它自己的Func;
类的指针:调用的是分配给它空间时那种类的Func;
4、对于一个类的对象和这个类的指针(用new运算符分配内存)在应用时有何区别
- 类和对象是两回事,对象是类的实例;
- 对象是在栈中分配的,使用new生成的对象是在堆中分配的;
- 要发挥虚函数的强大作用,必须使用指针来访问对象.
指针可以实现多态,直接用对象不行
执行定义对象,在栈空间,new的对象则是在堆
5、注意名字的类型.
一个是Student
一个是Student*
Student是直接访问一个对象
Student*是间接访问一个对象,因为通过了一个指针作媒介.
类型决定了你能做什么.
其实作用基本一样 都是为了调用类的成员变量 和成员函数用的
当你希望明确使用这个类的时候,最好使用对象,如果你希望使用C++中的动态绑定,则最好使用指针或者引用
指针和引用用起来更灵活,容易实现多态等
类的指针:他是一个内存地址值,他指向内存中存放的类对象(包括一些成员变量所赋的值).
对象,他是利用类的构造函数在内存中分配一块内存(包括一些成员变量所赋的值).
6、在应用时:
1.引用成员: 对象用" . "操作符; 指针用" -> "操作符.
2.生命期: 若是成员变量,则是类的析构函数来释放空间;若是函数中的临时变量,则作用域是该函数体内.而指针,则需利用delete 在相应的地方释放分配的内存块.
注意:用new 一定要delete..
当类是有虚函数的基类,f是它的一个虚函数,则调用f时:
类的对象:调用的是它自己的Func;
类的指针:调用的是分配给它空间时那种类的Func;
1.在类的声明尚未完成的情况下,可以声明指向该类的指针,但是不可声明该类的对象...
2.父类的指针可以指向子类的对象..
定义对象实例时,分配了内存。指针变量则未分配类对象所需内存
指针变量是间接访问,但可实现多态(通过父类指针可调用子类对象),并且没有调用构造函数。
直接声明可直接访问,但不能实现多态,声明即调用了构造函数(已分配了内存)。
至于那个效率高要看程序调用过程而定。
C++的精髓之一就是多态性,只有指针或者引用可以达到多态。对象不行
7、用指针的好处
第一实现多态。
第二,在函数调用,传指针参数。不管你的对象或结构参数多么庞大,你用指针,传过去的就是4个字节。如果用对象,参数传递占用的资源就太大了
3 const与static
static定义静态,包括静态变量、静态函数,类中的静态成员变量和成员函数等。主要是控制其访问和存储方式。
其中,静态变量包括局部变量和全局变量,静态局部变量是函数中定义,函数运行结束之后该变量不会被释放。静态全局变量在该文件范围内可以被访问。
类的静态成员和函数为该类所有,不依赖于具体的对象。静态成员函数只能访问静态变量。
const定义三种:
- const常量,定义时初始化,不能被修改。
- const形参,函数传递参数不能被修改
- const修饰类中的成员函数,该函数执行只读操作。
他们的作用:
static
(1)函数体内static变量的作用范围为该函数体,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
(2)在模块内的static全局变量和函数可以被模块内的函数访问,但不能被模块外其它函数访问;
(3)在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
(4)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。
const关键字的作用:
(1)阻止一个变量被改变
(2)声明常量指针和指针常量
(3)const修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;
(5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为”左值”。