这里的知识点并不全面,因为博主是以老师给的出题类型来复习的。
-
选择
- C++文件后缀名
.c:用C语言编写的源代码文件
.xpp或.cxx:用C++语言编写的源代码文件
.h、.hpp或.hxx:用C/C++语言编写的头文件,通常用来定义数据类型,声明变量、函数、结构和类
.obj:由编译器或汇编工具生成的目标文件,是模块的二进制中间文件
.exe:可执行文件
- if、for循环语句的反向表达式
- 关键代码的执行次数
循环体内的操作次数乘以n即为总时间复杂度
1+2+3+…+(n-1)=n(n-1)/2 n*n(n-1)/2
等差数列:Sn=n*a1+n(n-1)d/2 若公差d=1时:Sn=(a1+an)n/2
- 返回值
非void函数必须使用return语句来指定将要返回的值。
Void函数无返回值
- 函数重载的概念
是指在同一个作用域内,可以定义多个相同名称但参数列表不同的函数。这些函数可以具有不同的参数类型、参数个数或参数顺序,但是他们的函数名必须相同。 注意:重载函数的参数个数、参数类型或参数顺序,这三者中必须用一个不同。函数重载是一种多态性的表现形式,它允许在同一作用域定义多个同名函数,以便于程序员更加方便地调用不同的函数,从而提高程序的可读性和可维护性。
函数重载的规则:
● 函数名必须相同
● 参数列表必须不同(个数不同、类型不同、参数排序顺序不同等)
● 函数的返回值可以相同也可以不同
● 仅仅返回类型不同不足以成为函数重载
- 对象引用的使用方法:形参、实参
形参(形式参数)是在函数定义中出现的参数,它是一个虚拟参数,只有在函数调用时才会接收到传递进来的实际参数。形参可以被看作是一个占位符,在函数定义时并没有实际的数值,只有在函数调用时才会得到实参的数值。形参的主要作用是表示函数需要从外部传递进来的数据。
实参(实际参数)是在函数中实际出现的参数,它的值可以是常量、变量、表达式、类等。实参的值是确定的,必须在函数调用时提供。实参的主要作用是向函数传递数据,将数据的值传递给形参,在函数体内被使用。
形参和实参之间的传递方式有两种:值传递和地址传递。值传递是指将实参的值复制给形参,形参在函数内部使用时不会改变实参的值。而地址传递是指将实参的地址传递给形参,形参在函数内部使用时可以通过地址修改实参的值。
总结起来,形参是函数定义中的参数,是一个虚拟的占位符,用于接收函数调用时传递进来的实参。实参是函数调用时提供的具体数值,用于向函数传递数据。形参和实参之间的传递方式可以是值传递或地址传递。
- 类中的访问属性:缺省,不缺省
C++中,缺省参数(Default Argument)可以在函数声明和定义时指定一个默认值,这样在函数调用时,如果没有提供对应的实参,则使用默认值作为实参。如果提供了对应的实参,则使用提供的实参值。
返回类型 函数名(参数列表 = 默认值) { // 函数体 }
public继承:不改变基类成员的访问控制。
private继承:派生类所继承的基类成员的访问控制都变为private。
protected继承:基类中的private成员的访问控制不变,其余的都变为protected。
基类的 public成员被派生类继承,且在派生类中是可见的(visible in the derived class)。
基类的 private成员被派生类继承,但在派生类中是不可见的(not visible in the derived class)
- 公有继承、保护继承…到派生类当中的访问属性
派生方式 基类的public成员 基类的protected成员 基类的private成员
public派生 public成员 protected成员 不可见
protected派生 protected成员 protected成员 不可见
private派生 private成员 private成员 不可见
- 多态:动态、静态
多态按字面的意思就是多种形态,相同的方法调用,但是有不同的实现方式。多态性可以简单地概括为“一个接口,多种方法,实现接口与实现的分离。
静态多态:也称为编译期间的多态,编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),可推断出要调用那个函数,如果有对应的函数就调用该函数,否则出现编译错误。函数重载和运算符重载属于静态多态,复用函数名(编译期多态)
两种实现方式:函数重载,包括普通函数和成员函数的重载;函数模板的使用
动态多态(动态绑定):即运行时的多态,在程序执行期间(非编译期)判断所引用对象的实际类型,根据其实际类型调用相应的方法。派生类和虚函数实现运行时多态(运行期多态)
动态绑定
1.通过基类类型的引用或者指针调用虚函数
首先搞清楚这个对象的类型:
静态类型:对象声明时的类型,编译时确定
动态类型:目前所指对象的类型,运行时确定
2.必须是虚函数(派生类一定要重写基类中的虚函数)
动态多态的满足条件
1、有继承关系;
2、子类重写父类中的虚函数。
动态多态使用:
父类指针或引用指向子类对象
- 虚基类是干什么,作用
声明虚基类的一般形式为:
class 派生类名 :virtual 继承方式 基类名
作用:在继承间接共同基类时减少数据冗余,只保留一份成员。
注意:虚基类并不是在声明基类时声明的,而是在声明派生类时,指定继承方式声明的
虚基类,因为一个基类可以在生成一个派生类时作为虚基类,而在生成另一个派生类时不作为虚基类。为了保证虚基类在派生类中只继承一次,应当在该基类的所有直接派生类中声明为虚基类,否则会出现对基类的多次继承。在最后的派生类中不仅要负责对其直接基类进行初始化,还要负责对虚基类的初始化。
1, 一个类可以在一个类族中既被用作虚基类,也被用作非虚基类。
2, 在派生类的对象中,同名的虚基类只产生一个虚基类子对象,而某个非虚基类产生各自的子对象。
3, 虚基类子对象是由最新派生类的构造函数通过调用虚基类的构造函数进行初始化的。
4, 最派生类是指在继承结构中建立对象时所指定的类。
5, 派生类的构造函数的成员初始化列表中必须列出对虚基类构造函数的调用;如果未列出,则表示使用该虚基类的缺省构造函数。
6, 从虚基类直接或间接派生的派生类中的构造函数的成员初始化列表中都要列出对虚基类构造函数的调用。但只有用于建立对象的最派生 类的构造函数调用虚基类的构造函数,而该派生类的所有基类中列出的对虚基类的构造函数的调用在执行中被忽略,从而保证对虚基类子对象 只初始化一次。
7, 在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。
- 基类与派生类
派生类构造函数的要点总结:
首先创建基类对象;派生类构造函数应通过成员初始化列表(TableTennisPlayer(tp))将基类信息传递给基类构造函数;派生类构造函数应初始化派生类新增的数据成员(rating = r)。如果没有提供显示构造函数,将使用隐式构造函数。理解派生类对象创建过程的对象创建顺序:先创建基类对象,在创建派生类对象;释放对象的顺序与创建对象的顺序相反:首先执行派生类的析构函数,然后自动调用基类的析构函数。
派生类不能访问基类的私有成员,而必须通过基类方法进行访问。因此派生类构造函数必须使用基类构造函数;
派生类和基类的关系:(引用、指针)
派生类可以使用基类的方法,条件是方法不是私有的。
基类指针可以再不进行显式类型转换的情况下指向派生类对象。
基类引用可以再不进行显式类型转换的情况下引用派生类对象。
但是基类指针和引用只能调用基类的方法。
(1)公用派生类对象可以向基类对象赋值
由于公用派生类具有基类所有成员,所以把公用派生类的对象赋给基类对象 注意:在赋值时时舍弃派生类新增成员,所谓的赋值只对数据成员赋值,对成员函数不存在赋值子类型关系是单向的、不可逆的。B是a的子类型,而不能说a是b的子类型,只能用子类对象对其基类对象赋值,而不能用基类对象对其子类对象赋值,因为基类对象不包含派生类的成员,无法对派生类的成员赋值,同一基类的不同派生类对象之间不能赋值。
(2)公用派生类对象可以代替基类对象向基类对象的引用进行赋值或初始化 同样,公用派生类对象的地址可以代替基类对象地址向指向基类对象的指针进行赋值或初始化,即指向基类对象的指针也可以指向公用派生类对象。但是通过指向基类对象的指针只能访问公用派生类对象中基类成员,而不能访问公用派生类对象新增的成员。
(3)如果函数参数是基类对象或基类对象的引用,相应的实参可以使用公用派生类对象。同样,如果函数的参数是指向基类的对象的指针,相应的实参可以为公用派生类的对象的地址。
-
判断
- 派生类构造函数的调用顺序
派生类在构造时,先调用基类的构造函数 派生类构造函数的调用顺序是按照继承的层次自顶向下、从基类再到派生类的。类的构造,析构函数不能直接被继承,因此派生类构造函数总是先调用其直接基类构造函数再执行其他代码(其他代码遵循普通函数声明顺序进行构造)
- 构造函数特点
在创建一个新的对象时,自动调用的函数,用来进行“初始化”工作:对这个对象内部的数据成员进行初始化。
构造函数的特点 1)自动调用(在创建新对象时,自动调用)2)构造函数的函数名,和类名相同3)构造函数没有返回类型4)可以有多个构造函数(即函数重载形式)
构造函数的种类 1.默认构造函数2.自定义的构造函数3.拷贝构造函数4.赋值构造函数
无参的构造函数和全缺省的构造函数都称为默认构造函数,一个类中只能有一个默认构造函数,否则编译器无法理解调用那个默认的构造函数
- 私有继承、公有继承的访问权限
C++中有3种基本的继承方式,public继承、private继承、protected继承。不同的继承方式主要体现在,基类中的访问权限在派生类中的变化。
public继承: 意为公有继承。派生之后,原来基类中的访问权限到派生类中不变。基类中的public,派生类继承之后变成派生类的public。基类中的private,派生类继承之后依然是基类的private。基类中的protected,派生类继承之后变成派生类的protected。public继承是最为常用的继承方式。class BB :public AA{ };
private继承: 意为私有继承。派生之后,原来基类中的public、protected全部变成派生类的private。而基类的private依然是基类的private。与访问限定类似,如果不显示指定继承方式,则默认为private继承。class BB :private AA{ };或 class BB : AA{ };
protected继承: 意味保护继承。派生之后,原来基类中的public、protected全部变成派生类的protected。而基类的private依然是基类的private。
- 长度与空间大小比较
在c++中,length()只是用来获取字符串的长度。
在c++中,sizeof()用于获取数据类型或者变量所占内存空间的大小。可以通过sizeof计算获取数组元素个数。sizeof()用于返回括号中的对象在内存中分配的大小
char B[]={‘a’,’e’,’s’,’r’,’q’}; //求字符数组的所占的字节数
i=sizeof(B);
cout<<i<<endl; //结果为5*sizeof(char)=5
//求字符数组的元素个数
i=szieof(B)/sizeof(char); //结果为5
- 动态申请空间如何释放
malloc、calloc、realloc三个函数都可以用来动态申请内存,三者区别如下:
malloc函数(申请堆内存空间):分配指定的size字节并返回指向已分配内存的指针,内存没有初始化。返回类型为(void*)
calloc函数(配置内存空间并把该内存清空为0):为nmemb元素组成的数组分配内存,每个元素大小为size字节,并返回指向已分配内存的指针,内存会被初始化为0。nmemb --> 需要申请的内存块的个数 size --> 每一块内存的大小
函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0。
与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为全0。
realloc函数(重新分配内存空间):将ptr所指向的内存块的大小更改为size字节。如果内存比原来小,数据不会改变;如果内存比原来大,超出的那一部分内存没有被初始化。ptr 是要调整的内存地址
size调整之后新大小
返回值为调整之后的内存起始位置。
这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
free函数(释放原先配置的内存):释放ptr指向的内存空间。free函数用来释放动态开辟的内存,无返回值ree 释放堆空间
只是把内存空间归还给系统,没有把内存进行清空
也没有把指针指向NULL , 因此最好手动让指针指向 NULL避免野指针的出现
不允许释放其他区域的内存
new运算符
new 类型[ 初值 ];
用new分配数组空间时不能指定初值
delete运算符
delete p; delete[ ]指针变量;对数组空间的操作
student *p; p=new student; 指针直接赋值,两者类型要相同
delete p;回收p
如果由于内存不足等原因而无法正常分配空间,则new会返回一个空指针NULL,用户可根据该指针的值判断分配空间是否成功。注意:new和delete是运算符,不是函数,因此执行效率高,new要和delete配合使用。
- 静态变量的作用域
全局变量(静态储存)
从此程序开始到此程序结束都有效(这里指的是一个程序而不是定义它的文件)。
1、局部变量与全局变量重名
局部变量隐藏全局变量,变量值互不影响。
2、形参与全局变量重名
形参属于局部变量,符合局部变量隐藏全局变量,变量值互不影响。
全局变量与静态全局变量
全局变量与静态全局变量都是静态储存,那有什么区别呢?
1、非静态全局变量的作用域是整个源程序 ,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。
2、静态全局变量则限制了其作用域, 即只在定义该变量的源文件 内有效,在同一源程序的其它源文件(即声明了该变量的CPP文件,或包含该变量声明头文件的CPP文件)中不能使用它。
由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误
局部变量与静态变量
局部变量与静态变量不同的是它的存储方式即改变了它的生存期,static局部变量只被初始化一次,下一次依据上一次结果值;同时只要某个对象对静态变量更新一次,所有的对象都能访问更新后的值。
静态(static)函数与普通函数
static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
静态变量的作用域 是文件 ,只在这个文件中有效 静态局部变量 只会进行一次初始化
把局部变量改变为静态变量后是改变了它的存储方式,即改变了它的生存期。
把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。
因此static这个说明符在不同的地方所起的作用是不同的。应予以注意。
关于Static关键字
1.静态变量,分配在静态存储区,在数据段中。函数退出之后,变量值不变。
2.作用域,全局的静态变量、静态函数只能在本文件中使用。(不同于一般全局变量)
局部的静态变量同函数的局部变量
- 常引用的作用范围
常引用的格式:
const 类型 & 引用名;
常引用的作用是不希望对所引用的内容进行修改 利用这个性质,常引用可以作为函数的形参(常参数),来实现函数体内只能进行对变量的读取而不能改写的操作。
- 虚基类、虚函数概念
当在多条继承路径上有一个公共的基类,在这些路径中的某几条汇合处,这个公共的基类就会产生多个实例(或多个副本),若只想保存这个基类的一个实例,可以将这个公共基类说明虚基类。所以可以说,虚基类是为了只实例化一次基类存在的。
虚基类的特点
(1):虚基类构造函数的参数必须由最新派生出来的类负责初始化(即使不是直接继承).
(2)虚基类的构造函数先于非虚基类的构造函数执行。
虚函数的作用是实现动态联编,也就是在程序的运行阶段动态地选择合适的成员函数,在定义了虚函数后,可以在基类的派生类中对虚函数重新定义,在派生类中重新定义的函数应与虚函数具有相同的形参个数和形参类型。以实现统一的接口,不同定义过程。如果在派生类中没有对虚函数重新定义,则它继承其基类的虚函数.
class Student
{ virtual void show(){} }
class Student1 :public Student
{ void show(){} }
虚函数访问格式
指针变量->成员函数
关于虚函数的注意事项:
1、 必须把动态联编的行为定义为类的虚函数。
2、类之间存在父子类型关系,一般表现为一个类从另一个类公有派生而来。
3、 必须先使用基类指针或者引用指向子类型的对象,然后直接或者间接使用基类指针调用虚函数。
虚函数的作用:
可以让成员函数操作一般化,用基类的指针指向不同的派生类的对象时,基类指针调用其虚成员函数,则会调用其真正指向对象的成员函数,
而不是基类中定义的成员函数(只要派生类改写了该成员函数)。
-
填空
- 指针
指针的声明就是在变量类型名和变量名之间加上星号(*),并可以并可以任意选择让星号紧贴类型名 如果指针没有初始化,它可能指向一个未知的地址,那么我们在尝试读取数据的时候就可能造成程序崩溃。此外,在指针初始化的时候,不能使用0以外的整型给指针赋值。
c++还有一种通用的void*指针。我们知道指针就是地址,指针的类型不过表示了地址指向的位置所存储的数据类型。如果我们将int * 指针转换为float * 指针,那么程序也只是将数据重新解读为浮点类型。所以这里void * 只是代表了一个地址,而我们不知道它所指向的数据类型,但我们也可以重新定义它所指向的数据类型。void * 一般会在一些内存处理的系统函数时候使用。
一个对象存储空间的起始地址就是对象的指针。可以定义一个指针变量,用来存放对象的地址,这就是指向对象的指针变量。 类名* 对象指针名;
指向对象成员的指针: 数据类型名 *指针变量名;
定义指向对象成员函数的指针: 数据类型名(类名::*指针变量)(参数列表);
指针变量名=&类名::成员函数名;
指向当前对象的this指针:其值是当前被调用的成员函数所在对象的起始地址。 *this就是指this所指的对象。
- 常量:常变量、常引用、常函数
常对象 类名 const 对象名 const 类名 对象名 // 以下两种形式两种都可以
所有成员的值都不能修改,(所有数据成员都是常数据成员)(非要改变成员变量用mutable标记成员变量)const对象只能调用const成员函数,不能调用非const函数,但类中可能有非const函数,但不能用 。必须要有初值
常数据成员
必须用参数初始化表初始化,因为其不能赋值
能被常/非常成员函数引用
常成员函数 void f1() const //const加后面
类型名 函数名(参数表)const
只能引用本类中数据成员,不能修改它们
能引用常/非常数据成员
指向对象的常指针
指针变量声明为const型,不能改变指针指向,但能改变指向对象的值
类名 *const 指针变量名
常作为函数形参,不允许函数在执行过程中改变指针变量的值,让其始终指向原来的对象。
如果函数执行过程中修改了该形参,编译系统会发现错误,比人工保证形参不被修改更可靠
指向常对象的指针
指向的对象内容(ex:成员变量)不能通过指针改变
const 类名 *指针变量名
区分:指向对象的常指针 Time *const p
指向常对象的指针 const Time *p 指向的对象不能变
一个对象已被声明为常对象,只能用指向常对象指针指向
指向常对象的指针,可以指向一个非const对象,但其指向的对象不能通过指针来改变,如果希望任何情况下s1的值都不能改变,应把它定义为const型(指向的对象不能通过指针来改变的原因:用指向常对象的指针指向变量期间,变量也有常变量特征,其值不能改变)
指向非const对象的指针,不能指向const对象(形参是const对象的指针,实参不能指向const对象)(原因:形参指针和实参指针指向同一变量,形参所指向的变量的数是可以改变的,但实参是const变量的地址,指向的变量的值是不可改变的,发生矛盾)
形参 实参
非const对象的指针 只能非const对象的指针 可以改变值
const对象的指针 const/非const对象的指针 不可改变值
指向常对象的指针常用于函数形参,保护形参所指的对象,让它在函数执行过程中不被修改 指向常对象的指针变量,不能改变指向对象的值(内容),可以改变指针指向的值
对象的常引用
void f1(const ClassName &point)
不希望在函数中修改实参s1的值,可以把引用变量student修改为const(常引用)
在函数f1()中不能修改student的值,也不能改变实参s1的值
常量指针通常有两种表示方法:
method 1: const double * ptr;
method 2: double const *ptr;
指针常量通常表示为:
double * const ptr;
读法:
const double * ptr;//按照顺序读作常量指针:指向常量的指针--即所指向的值不能变.
double const * ptr;//同上
double * const ptr;//按照顺序读作指针常量,指针是一个常量--指针的值不能变.
常量换成类的对象,读法一样
// 指向对象的常指针
Time *const p // 指向的对象内容(ex:成员变量)不能通过指针改变
// 指向常对象的指针
const Time *p // 指向的对象不能变
首先读“指向”,然后碰到什么读什么,
碰到对象类型读指向对象的常指针,
碰到const,读指向常对象的指针。
理解
*后为一整体,有const为常指针,无const为常对象/量
const double * ptr; // 指向常量的指针
double * const ptr; // 指向变量的常指针
Time *const p; // 指向对象的常指针
const Time *p; // 指向常对象的指针
- 构造函数初始化的两种方式(参数初始化表、用带参数的构造函数)
构造函数的初始化列表
构造函数的初始化列表可以在构造函数的参数列表后面使用冒号分隔符,然后在冒号后面列出每个成员变量及其对应的初始化值。
class MyClass
{ public:
MyClass(int value) : m_value(value) {}
private:
int m_value; };
在构造函数体内赋值
构造函数也可以在其函数体内使用赋值语句来初始化成员变量。
class MyClass
{ public:
MyClass(int value) { m_value = value; }
private:
int m_value; };
注意的是,使用初始化列表进行成员变量初始化比在构造函数体内使用赋值语句效率更高,并且可以用于初始化const成员变量或引用类型成员变量。
4. 对象数组的定义
所谓对象数组,指每一个数组元素都是对象的数组,即若一个类有若干个对象,我们把这一系列的对象用一个数组来存放。对象数组的元素是对象,不仅具有数据成员,而且还有函数成员。
声明一个一维数组的格式如下: 类名 数组名[常量表达式]
与基本数据类型的数组一样,在使用对象数组时也只能访问单个数组元素,其一般形式为:
数组名[下标表达式].成员名 com[0].abscomplex()
在建立数组时,需要对数组进行初始化。对象数组的初始化过程实际上就是调用构造函数对每一个元素对象进行初始化的过程。 当一个数组中的元素被删除时,系统也会调用析构函数。
对象数组的特性
在建立对象数组的时候需要调用构造函数,有多少个对象就要调用多少次构造函数。
如果对象数组含有析构函数,那么建立对象数组时按每个元素出现的顺序调用构造函数,按相反的顺序调用析构函数。
对象数组的初始化究竟是什么形式,本质上取决于所属类的构造函数。
如果对象数组所属类有带参数的构造函数,可用初始化列表按顺序调用构造函数,使用复制初始化来初始化每个数组元素。
如果对象数组所属类有单个参数时,定义数组时可以直接在初始化列表中提供实参。
对象数组创建时若没有初始化,则其所属类要么有合成默认构造函数,要么定义无参数的构成函数或全部参数为默认参数的构造函数。
构造函数没有参数
各元素对象的初值要求为相同的值时,应该在类中定义不带参数的构造函数或者是默认构造函数。
Complex com[3]={ //定义对象数组
Complex(1.1,2.2), //调用构造函数,为第1个对象数组元素提供实参1.1和2.2
Complex(3.3,4.4), //调用构造函数,为第2个对象数组元素提供实参3.3和4.4
Complex(5.5,6.6) //调用构造函数,为第3个对象数组元素提供实参5.5和6.6 };
C++中使用对象数组的主要原因是可以方便地创建多个对象,并且可以通过下标访问和操作这些对象。对象数组可以用于存储和处理大量的数据。此外,对象数组还可以用于实现一些数据结构,例如队列、栈等。在使用对象数组时,需要注意对象的构造函数和析构函数的调用,以及对象数组的初始化方式。
5.各种指针的使用输出:指针嵌指针
指针的每一次递增,都会指向下一个元素的地址
指针的每一次递增,都会指向上一个元素的地址
指针在递增和递减是移动的大小决定于指针的类型
double类型的指针为8字节
int类型的指针为4字节等.
指针使用算术运算符和关系运算符可以对数组进行访问.
因为数组名类似于一个指针常量,我们无法对一个数组进行自增自减操作,所以我们经常使用指针代替数组.
指针同样可以指向指针,即指针链,比如:指针a指向指针b,指针b指向值c;
学习资料来自CSDN、B站。