C++知识点总结

一、 第一节

一,C++课程内容:
1,从C迁移到C++ (C和C++的联系和区别)
2,C++中的结构体与类
3,深入理解C++语言中类的设计
4,C++中的继承
5,C++中的多态
6,C++中的运算符重载
7,C++模板
8,C++中的输入输出流
9,C++智能指针,C++中的异常处理。
10,STL(标准模板库)

C++三大特性:封装,继承,多态

二,什么是C++?
1,什么是C++?
(1) C++是在C语言的基础上发展起来的,C++继承了C语言的全部功能。
(2) C++语言完全兼容于C,C++环境下可以和C语言编写的代码一起编译。
(3) C++对C进行了扩充,增加了面象对象的语法结构。
(4) C语言的语法结构适合于开发面向过程的程序,而C++的语法结构适合于开发面向对象的程序。
(5) Linux系统中用gcc编译C语言代码,用g++编译C++代码。
2,C++文件的命名方法
(1)头文件:.h, .hpp, .hxx
(2)源文件:.cpp, .cc, .cxx, .C
三,C++对C的扩展
1,对结构体的扩展
(1) 在结构中可以定义函数。
(2) 权限访问限定符 : public, protected, private .
2,C++中的类以及封装性
封装:这种把函数和变量集中在一个类中,且函数用来操作变量的做法叫封装,函数称为类的成员函数,变量称为类的成员变量,封装是C++的三大特性之一。
为什么需要封装:1,人性化设计;2,保护数据成员,避免外部破坏对象的数据;3,模块化便于管理。
在面向过程思想中,因为数据和函数是相互分离的,某些函数有可能错误地修改了不属于它的数据,从而造成程序错误。
而在面向对象思想中,数据和操作(函数)被捆绑在一起构成了对象,而数据可以是某个对象所私有的,
只能由与它捆绑在一起的操作(函数)访问,这样就避免了数据被其他函数意外地访问
3,默认参数
默认参数:在定义函数的时候,可以给参数一个默认值,如果传递实参,该参数的值就是实参的值,如果没有传递实参,该参数就使用其默认值。
注意:****(1) 可以有多个默认参数,但是默认参数的默认值的设置顺序必须按照从右到左的顺序依次设置,中间不能间断。
(2) 实参在给形参传值是从左到右进行传递的。
(3) 函数在声明的时候有默认参数,在调用函数的时候就可以省略默认参数的值
(4) 默认参数和函数重载一起使用,容易产生二义性。
4,函数重载
在同一个作用域内,函数名称相同,参数列表不同的函数之间叫函数的重载,和返回值无关。
***为什么C++支持重载,而C语言不支持重载?
C语言编译函数时只把函数名作为唯一的识别符号。
而C++编译函数时会把函数名和参数列表结合起来作为函数的符号名。
C++中类的成员函数可以重载,全局函数可以重载。
通过反汇编命令可以查看生成的函数符号。
反汇编命令:
objdump -t <可执行文件名> > <保存文件名>
5,void *类型指针的不同
在C语言中 void *类型的指针可以赋值给任何类型的指针。
在C++中void 类型的指针需要进行类型转换才能赋值给其它类型的指针。
6,C++中的赋值方法
C语言中的赋值只有一中即: = ;
C++中除了使用 = 外,还可以使用(),比如:int k(10)。
7,C++引用运算符&
*****
什么是引用?引用就是一个变量的别名,实际上就是一个变量有两个甚至多个名字,比如把星期一,叫礼拜一,把猫叫咪一样,
引用实际上与引用的变量占用的是同一个内存空间
对引用的操作等价于对变量的操作,不会给引用分内存空间,引用和被引用的变量共享一块内存空间。
一个变量可以有多个引用,一个引用只能对应一个变量(一个引用不能是多个变量的别名)。
8,引用与指针的区别:
(1)指针是一个实体,需要分配内存空间。引用只是变量的别名,不需要分配内存空间。
(2)引用在定义的时候必须进行初始化,并且在初始化后不能够改变引用的目标。指针在定义的时候不一定要初始化,并且指向的内存地址是可变的。(注:引用的值不能为NULL)
(3) 指针和引用的自增运算结果不一样。(指针是指向下一个空间,引用时引用的变量值加1)
(4) sizeof 引用得到的是所引用的变量(对象)的大小,而sizeof 指针得到的是指针本身的大小。
(5) 引用访问一个变量是直接访问,而指针访问一个变量是间接访问。
(6) 可以有void
型指针,不能有void型的引用。
(7) 有数组指针,函数指针,不能有数组引用,和函数引用。
(8) 可以定义多级指针,但不能定义多级引用,如: 数据类型 && 引用名 = 变量名;
(9) 引用也可以有别名(引用),对别名的操作就是对引用的操作。
9, const 修饰符
const关键字在C++的类成员函数后面,表示这个函数不能改变类中的任何属性(即成员变量)。
如:void unchange() const; 但在C中不能有这样的声明。
10, mutable关键字,成员变量声明时在前面加上mutable关键字的,可在const修饰的函数中修改其值。
11,构造函数和析构函数
(1) 构造函数:
***<1> 构造函数与类同名,没有返回值;
***<2> 是在对象被创建时由系统自动调用;
***<3> 构造函数的作用是用来初始化该对象。
<4> 若定义类时未提供任何构造函数,则C++会为此类提供一个不带参数的默认构造函数。
若定义类时有提供构造函数,则系统会使用提供的构造函数对对象进行初始化。
<5> 构造函数可以重载。
<6> 在用类定义对象时,若不给该对象传任何参数,那么在对象名后面不要加(),否则此代码会被忽视。
(2) 析构函数
***<1> 构造函数在类名前加 ~
***<2> 在对象被释放时调用。
***<3> 释放对象所占用的资源。
***<4> 析构函数不能有参数。
***<5> 析构函数不能被重载。
12,C++中的内存分配
用new 分配内存,用delete释放new分配的内存
***(1) new和delete 是运算符,不需要加头文件, 而malloc和free是C的库函数,需要加头文件。
(2) malloc 返回值是void
型指针,需要强制类型转换才能使用。new返回值是特定数据类型的指针,不需要强转。
(3)new 申请内存的时候已经确定内存中存放的数据类型,new 会做类型检查 ,即指针的数据类型必须和new 后面的数据类型一致。
(4)malloc 申请内存大小需要指定大小,new只需要确定数据元素的个数和类型。
****(5)用new 创建一个对象时会自动调用该对象的构造函数,delete一个对象指针时,会自动调用该对象的析构函数。而malloc free 没有这个功能(最重要的区别)
//new 申请一个数组的内存空间: 数据类型 *指针名 = new 数据类型[数组大小];
//new 申请一个元素的内存空间: 数据类型 *指针名 = new
char * parr = new char[32];
delete []parr;
13, this指针
在类的成员函数中,this指针是一个隐含参数,this指针指向调用该函数的对象本身,this指针是一个常量指针。
作用:
(1) 当成员函数的形参名称 和 成员变量名称相同时,可以用this指针区分成员变量和形参。
四,C++中的作用域
:: 作用域操作符,它的左边代码 域名称,右边可以是变量或函数,右边的变量或函数是属于左边这个域范围内的。
(1) 局部域(函数域)
(2) 全局域
(3) 文件域(只能在本文件范围内使用)
(4) 类域
(5) 命名空间(名字空间) : namespace
命名空间定义 :名字空间是一个作用域,作用是为了防止名字冲突。
namespace [名称]
{
<变量>

<函数>
};
using namespace <名字空间名称>;
在该文件中使用该名字空间中的变量或函数时,可以不用加 该名字空间的作用域作为前缀。
匿名名字空间,相当于在变量或函数名前面添加了static,只能在本文件范围内使用
namespace
{
<变量>

<函数>
};

二、第二节

一,类的静态变量,和静态成员函数 – 在类中定义变量或函数前加static就构造了静态成员变量或静态成员函数
1,类的静态成员变量 (1),静态成员变量在程序启动时已经在全局数据段中为其分配了内存空间,静态成员变量相当于全局变量,只是在所属类的作用域内。
(2),用sizeof( )来获取类的大小时,静态变量不计算在内。
2,类的静态成员函数:
(1) 类的静态函数相当于全局函数;
(2) 类的静态函数中不能使用this指针;
(3) 类的静态成员函数在类的作用域内。
3,类的静态成员
(1) 类的静态成员(包括静态成员变量和静态成员函数)和该类生成的对象无关,只受类的作用域限制。
(2),类的静态成员变量访问方式 类名::静态成员名,而不能使用对象来访问 (新版的C++允许通过对象名加".“的方式访问类的静态成员)。
(3),类的静态成员在使用前必须被初始化。
二,C++的继承
1,继承的格式,其中类child 继承自类parent
class child : [public/protected/private] parent
{
//类child 继承自 parent类,其中parent类叫做父类,child类叫做子类
//也可以称为类parent派生了child类。

};
基类/父类:指的是顶层的类,
子类:相对于父类或基类,
关系:子类继承了父类/基类,基类/父类派生了子类。
2,继承关系中构造函数的执行顺序: 先父类后子类
3,继承关系中析构函数的执行顺序: 先子类后父类
4,成员初始化列表
1) 用于初始类的的成员变量
2) 用于子类的构造函数给父类的构造函数传参
3) 类的常量成员和引用成员必须使用成员初始化列表进行初始化,(常量指针可以不使用成员初始化列表的方式进行初始化);
4)成员初始化列表的执行顺序,是按照变量在类中的声明顺序执行进行初始化,而与成员初始化列表中的初始化顺序无关,若有向父类的构造函数传递参数,则最先执行父类的构造函数。
四,继承关系中的权限访问限定符
1,若子类继承父类时未指明以什么样的权限继承,默认是以private权限继承。
2, 具有protected权限的类成员,只能由该类或者该类的子类访问。
继承关系中权限访问限定符的作用是,子类把父类的成员(包括函数和变量)以什么样的权限来继承。
子类继承父类时其成员函数和变量的权限设定原则,权限最小原则,即:继承关系中,对于变量和函数的权限设定是,子类继承时的权限设定 与 父类的成员权限设定进行比较,哪种权限最严格,在派生类中对该变量和函数就设定为哪种权限。
五,拷贝构造函数
1,拷贝构造函数也是构造函数,与普通的构造函数的区别为它的参数为该类 类型的常量引用。
//参数为引用,不为值传递是为了防止拷贝构造函数的无限递归,最终导致栈溢出
2,如果没有为类提供自定义的拷贝构造函数则系统自动生成一个默认的拷贝构造函数,此默认的拷贝构造函数为浅拷贝。
3,当采用直接初始化,即用”()" 或 赋值(=)初始化实例化对象时系统会自动调用拷贝构造函数,注意,除非在对象创建时,通过直接初始化“()”或者 赋值初始化"="时会调用拷贝构造函数,除此之外用同类 型 的对象给另一个对象赋值时不会调用拷贝构造函数,只是简单的内存拷贝。
4,浅拷贝
(1)在类的拷贝构造函数中,对于指针成员,只是简单的赋值,并没有为新对象的指针重新分配内存,导致两个对象的指针成员指向同一内存。
(2)当其中的一个对象的指针成员所指向的内存被释放后,导致另一个对象无法继续使用其指针成员指向的同一块内存。
5,深拷贝
(1)在拷贝构造函数中,对于指针成员为其重新分配了内存空间。并把旧对象的指针指向的内存内容拷贝到了新对象分配的内存中。
(2)释放一个对象的指针成员时,不影响另一个对象。
6,拷贝构造函数的调用时机
(1) 通过()进行初始化新对象
CPerson per1(“Mark”, 25, “man”);
CPerson per2(per1); //会调用拷贝构造函数

  1. 通过赋值操作符赋值时会调用
    CPerson per1(“Mark”, 25, “man”);
    CPerson per2 = per1; //会调用拷贝构造函数
    (3) 若函数的参数是类类型,则调用该函数时,会为参数执行拷贝构造
    void print_person(CPerson per)
    {
    cout << “name:” << per.getName() << “, age:”
    << per.getAge() << “, sex:” << per.getSex() << endl;
    }
    int main()
    {
    CPerson per1(“Mark”, 25, “man”);
    print_person(per1); //会调用拷贝构造,该函数执行完成后会调用析构函数。
    }

    7,不会调用构造函数的场景
    (1) CPerson per1(“Mark”, 25, “man”);
    CPerson per2;
    per2 = per1; //不会调用拷贝构造函数,但会进行内存拷贝,把per1的内存复制给per2的内存。

    (2) 函数的参数为类的引用类型不会调用拷贝构造函数
    void print_person(CPerson & per)
    {
    cout << “name:” << per.getName() << “, age:”
    << per.getAge() << “, sex:” << per.getSex() << endl;
    }

    int main()
    {
    CPerson per1(“Mark”, 25, “man”);
    print_person(per1); //不会调用拷贝构造,该函数执行完成后也不会调用析构函数。
    }

    (3) 用explicit关键字声明构造函数的作用

    若一个类有具有一个参数的构造函数,在用此类来定义对象时,可以用与带一个参数的构造函数的参数类型相同的数据 为新定义的对象赋值,在定义该对象时会调用带一个参数的构造函数。

    explicit的作用就是禁止上面的这种赋值。禁止为在创建对象时用赋值运行符为对象赋值。

六,可以把子类对象赋值给父在对象,不能把父类对象赋值给子类对象。

三、第三节

一,向上类型转换和向下类型转换
1,子类对象的指针赋值给父类对象指针时会自动进行默认的类型转换,把子类类型转换为父类类型,这种类型转换会自动进行,称之为向上类型转换,它是安全的。
2,父类类型的对象指针赋值给子类类型的对象指针时,要进行强制类型转换,这种转换称之为向下类型转换,并且这种转换是不安全的。
3,例子:
对CPerson::doWork()前面没有加virtual
1),CPerson和CWorker这两个类是继承关系,CPerson是基类,CWorker是子类。
2),CPerson通过sizeof求大小,结果是44 Byte, 而CWorker通过sizeof求大小结果是76
CWorker的大小是 CPerson的大小 + CWorker::jo[32] = 76
3),把CWorker对象地址转换成CPerson类型的指针,并通过这个指针调用doWork()时调用的是CPerson::doWork()
4),把CPerson对象地址转换成CWorker类型的指针,并通过这个指针调用doWork()时调用的是CWorker::doWork()
对CPerson::doWork()前面加上virtual关键字
1),CPerson和CWorker这两个类是继承关系,CPerson是基类,CWorker是子类。
2),CPerson通过sizeof求大小,结果是48 Byte, 而CWorker通过sizeof求大小结果是80
CWorker的大小是 CPerson的大小 + CWorker::jo[32] = 80
3),把CWorker对象地址转换成CPerson类型的指针,并通过这个指针调用doWork()时调用的是CWorker::doWork()
4),把CPerson对象地址转换成CWorker类型的指针,并通过这个指针调用doWork()时调用的是CPerson::doWork()

二,多态
基类有虚函数的,当用基类指针指向派生类对象时,不会进行类型转换。
而基类没有虚函数的, 当用基类指针指向派生类对象时,会隐含的进行向上类型转换。
1,若基类中无虚函数,当用基类指针指向派生类对象时,会自动进行类型转换,把派生类指针转换成基类指针,调用基类和子类都实现的函数时实际调用的是基类的函数,这种转换发生在编译阶段,这也叫编译时绑定或早绑定。
2,若基类中有虚函数,当用基类指针指向派生类对象时,不会进行类型转换,调用虚函数时会根据虚函数表中的地址进行调用,这也叫运行时绑定或绑定。
3,虚函数表(有虚函数的类,在用该类或者该类的子类创建对象时会生成虚函数表)
(1)基类中若有虚函数(函数前面加了virtual关键字),则以这个类生成的对象或者其派生类生成的对象中就有一个虚函数表指针。
(2)虚函数表中存放的是虚函数的地址,即虚函数指针。
(3)虚函数可以继承,若父类中的某函数为虚函数,则派生类中的该函数也为虚函数。
(4),虚表可以继承,如果子类没有重写基类的某个虚函数,那么子类的虚函数表中保存的是基类该函数的地址,如果子类重写了基类相应的虚函数,那么子类的虚函数表中保存的就是子类自身的虚函数实现,如果子类新增了自己的虚函数,那么虚表中也会添加该项。
(5),派生类的虚表中虚函数的排列顺序和基类的虚表中虚函数地址排列顺序相同。
3,虚函数的顺序
无虚函数覆盖
1)虚函数按照其声明顺序放于表中。
2)父类的虚函数在子类的虚函数前面。
有虚函数覆盖
1)覆盖的函数被放到了虚表中原来父类虚函数的位置。
2)没有被覆盖的函数依旧。
4,多态定义:基类有虚函数的,当用基类指针指向派生类对象,在调用该虚函数时,实际调用的是派生类虚函数.表中实际所存放的虚函数。
C++中的多态是依赖虚函数表来实现的。
5,多态的作用
(1)应用程序不必为每一个派生类编写功能调用,只调用抽象基类的方法就能达到调用派生类功能的目的,大大提高程序的可复用性,减小了模块之间的耦合性。//继承
(2)不同的各个派生类的功能可以被基类的同一个方法调用,可以提高程序可扩充性和可维护性。
三,多重继承中的重复继承的问题
基类被两个子类继承,而孙子类又分别继承这两个子类 ,从而引出了重复继承的问题,导致基类对象在孙子类中有两份,当孙子类对象访问顶层基类中的成员时会出现二义性错误,
可通过指定孙子类父类的方式 解决这个错误。
四,虚拟继承
虚拟继承解决了多重继承中出现的重复基类的问题。
五,纯虚类(抽象类,接口类),用来定义规则,规则即函数。
1,如果类中定义了一个虚函数,函数尾部添加"=0;",则此函数为纯虚函数,纯虚函数不需要实现。
2,如果一个类中有至少一个纯虚函数,那么这个类就是纯虚类。
3,纯虚类不能用来实例化对象。
4,派生类如果没有实现父类的所有纯虚函数,则派生类也为纯虚类,也不能实例化对象。

四、第四节

一,友元函数
友元函数的声明可以放在类的私有部分,也可以放在公有部分,它们是没有区别的,都说明此函数是该类的一个友元函数。
一个函数可以是多个类的友元函数,需要在各个类中分别声明。
友元函数的调用与一般函数的调用方式和原理一致。
友元函数不属于类的成员函数。
在友元函数中可以访问宿主类的中用private\protected限定的成员。
一个类与一个函数的友元关系不能被该类的子类继承。
声明友元类的方法:
class XXX
{
friend <函数原型>;
};

explici
友元类是声明一个类成为该类的友元类,在该友元类的成员函数中可以访问宿主类的中用private\protected限定的成员。
class XXX
{
friend class <类名>;
};

explici
友元成员函数,即把一个类的某个成员函数可以声明为宿主类的友元。这样,在这些友元成员函数中就可以访问宿主类的中用private\protected限定的成员。

class XXX
{
	friend  <返回值> <类域::函数原型>;
};

四,运算符重载(操作符重载)
运算符重载的实质就是函数重载的一种形式,目的是能够用系统已经定义好的运算符对自定义的数据类型进行运算,运算的规则由重载的运算符函数来实现。
1.运算符函数形式:
<返回值类型> operator <运算符符号> (<参数表> )
{
<函数体>
}
2.可以被重载的运算符:
算术运算符:+、-、、/、%、++、–
位操作运算符:&、|、~、^(位异或)、<<(左移)、>>(右移)
逻辑运算符:!、&&、||
比较运算符:<、>、>=、<=、==、!=
赋值运算符:=、+=、-=、
=、/=、%=、&=、|=、^=、<<=、>>=
其他运算符:[]、()、->、,、new、delete、new[]、delete[]、*指针运算符
3.不能被重载的运算符:
逗号(".") 、 问号(?)
三目运算符(?😃、siezof、作用域运算符(:😃
4,一般来讲,单目运算符最好重载为成员函数,双目运算符最好重载为友元函数。=、[]、()、->以及所有的类型转换运算符只能作为成员函数重载。
5,this指针的作用
在类的成员函数内部,this指针是隐含传递给成员函数的参数,它指向调用这个成员函数的对象,它是一个常量批量。
(1),当形参和成员变量名字相同时,可以用来区分成员变量和形参。
(2),在成员函数需要返回对象自身时,可以返回this。

五,输入输出流
1, 什么是流?流就是缓冲区中的数据。
(1),C++中一共有四种流的状态,这些流状态标志位都是ios类的成员,如下:
1) badbit 发生了致命性错误,流无法继续使用
2) eofbit 输入结束,文件流物理上结束了,或者是用户按下ctrl+z或ctrl+d结束
3) failbit io操作失败,主要是操作了非法数据,流可以继续使用,输入结束后也将设置流为failbit。
4) goodbit 一切正常,没有错误发生
(2), C++同时也提供了一系列的函数用于测试这些状态位和操作状态位。
good() 判断流是否是goodbit状态
bad() 判断流是否是badbit状态
eof() 判断流是否是eofbit状态
fail() 判断流是否是badbit或failbit状态
clear() 清除流的状态
这些都是io流类的成员函数,无论是标准输入输出流还是文件流还是字符串流,都是有这一系列的函数

(3),setstate(ios:XXX) //设置流的状态
2,标准的输入输出流:
“<<”和“>>”本来在C++中是被定义为左位移运算符和右位移运算符的,由于在iostream头文件中对它们进行了重载,使它们能用作标准类型数据的输入和输出运算符。所以,在用它们的程序中必须用#include命令把iostream包含到程序中。

五、第五节

一,模板
C++中模板是支持参数化多态的工具,就是让类或者函数声明为一种通用类型,使得类中的某些数据成员或者成员函数的参数、返回值在实际使用时确定类型。使用模板的目的就是能够让程序员编写与类型无关的代码,模板也是泛型编程的基础。
模板是一种对类型进行参数化的工具,通常有两种形式:
1,函数模板
函数模板针对仅参数类型不同的函数,参数类型不一样的但是功能及函数名一致的函数定义方式如下:
template <class 形参名,class 形参名,…> 返回类型 函数名(参数列表
{
函数体
}
2,类模板
类模板针对仅数据成员和成员函数类型不同的类,成员属性的类型和成员函数的类不一样但是成员属性及函数一样的类。
template<class 形参名,class 形参名,…> class 类名
{ …
};
注意:
(1)模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。
(2)声明数据类型参数标识符的关键字既可以用class也可以用typename。
3,模板的类型参数
模板形参表示的是一个未知的类型。模板类型形参可作为类型说明符用在模板中的任何地方,与内置类型说明符或类类型说明符的使用方式完全相同,即可以用于指定返回类型,变量声明等。类型形参仅由关键字class或typename后接说明符构成。
4,模板的非类型参数
模板的非类型参数,可以为C++内置的类型,或者自定义的类型。
5,模板的默认类型参数
类模板可以具有类型或值形参的默认实参。使用等号 (=) 后跟类型名称或值来指定默认参数。对于多个模板参数,第一个默认参数后的所有参数必须具有默认参数。声明带默认参数的模板类对象时,请省略参数以接受默认参数。如果没有非默认参数,请不要忽略空尖括号

六、第六节

一,内联函数
1,在成员函数的前面加上inline关键字的函数就是内联函数。
2,编译器在对inline函数进行编译时不会让程序进行跳转,而是直接把inline函数的代码在声明处展开,这样可以对一些频繁调用的函数进行优化,提高执行效率。
二,虚析构函数的作用
当用基类指针,指向动态分配的派生类对象时,在用delete 来删除基类指针所指向的对象时,能够保证派生类的析构函数被调用,从而达到释放派生类对象的目的,否则,如果基类的析构函数不是虚函数,在释放基类指针指向的对象时,派生类析构函数不会被调用。
三,C++中的强制类型转换
C++中的强制类型转换共有4个关键字分别是:static_cast,const_cast, reinterpret_cast,dynamic_cast。
1,static_cast;
static_cast 用于不同类型之间的指针转换。 (1)用于具有继承关系的类层次结构中子类指针转换为基类指针,这称为进行下行转换,由于没有动态类型检查,所以是不安全的。
(2)把一种指针类型转换为另一种指针类型,其安全性也需要程序员来保证。
2,const_cast;
用来修改数据的const或volatile属性,增加或者去除const或者volatile属性。
3,reinterpret_cast
用于其值是算术数据类型之间的类型转换,比如指针与指针,或指针与内置的算术数据类型之间。 比如该运算符把某种指针转换其他类型的指针。它可以把一个指针转换为一个整数,也可以把一个整数转换为一个指针。比static_cast可转换的类型更广泛。
4,dynamic_cast
用于继承关系中父类指针与子类指针之间的转换,在转换时会进行安全性检查,若存在向下类型转换,或者两个类之间无继承关系则转换失败返回NULL。
(1)安全的基类和子类之间转换。
(2)继承关系中的父类对象指针转换为子类指针时必须要有虚函数。
(3)相同基类不同子类之间的交叉转换,但结果是NULL。
(4)无关类 类型指针之间的转换,结果是NULL。

六、day6

一,C++异常处理
1,语法错误(编译错误):比如变量未定义、括号不匹配、关键字拼写错误等等编译器在编译时能发现的错误,这类错误可以及时被编译器发现,而且可以及时知道出错的位置及原因,方便改正。
2,异常(运行时错误):比如数组下标越界、例如内存访问非法、除法中除数为零等等,这类错误不易被程序员发现,它能通过编译且能进入运行,但运行时会出错,导致程序崩溃,这种程序运行时出现的不正常现象就是异常。

3, 在C++中,异常的抛出和处理主要使用了以下三个关键字:try、 throw、 catch
如果存在不同类型的异常
try{

//可能产生异常的代码或函数调用,包含可能抛出异常的语句;

}catch(异常类型1 [形参名]){
	//可能出现的异常1
}catch(异常类型2 [形参名]){
	//可能出现的异常2
}catch(...){
	//如果不确定异常类型,在这里可以捕获所有类型异常!
}

4,C++中使用异常处理
1)try作用域中需要包含可能发生异常的代码。
2)需要判断代码是否会发生异常,若会发生异常则用throw用来抛出一个异常。
3)抛出异常后,代码会中断当前的执行流程,转到与抛出的异常类型匹配的对应catch域来运行异常处理程序。
4)若匹配到了对应的catch域,则执行该catch域的异常处理代码,执行完异常处理后,继续执行该异常处理后面的代码。
5)若该异常没有对应的catch域,则程序崩溃。
	
5,异常处理规则
1)当我们的程序抛出异常时,会先中止当前函数的执行,开始查找与该try域对应匹配的catch语句;
	2)如果与产生异常的代码,在作用域上有同一级的异常捕获块,同一级的try-catch,则尝试去匹配;
	2)如果有匹配的,则进行处理;
2)如果没有与之匹配的异常处理,则退出当前函数栈,继续在上一级的try-catch作用域中进行从查找匹配的catch域;
3)不断重复上述过程;
4)如果到达main函数栈,依旧没有匹配,则直接终止程序。
上述沿着调用链查找匹配的catch子句的过程称之为栈展开:找到匹配的catch子句并处理以后,会继续沿着catch子句后面继续执行。

6.	异常捕获的匹配规则
异常对象类型与catch说明符的类型必须完全匹配,只有以下几种情况例外:
1.允许从非const对象到const类型对象的转换.
2.允许派生类型到基类类型的转换.
3.将数组转换为指向数组类型的指针,将函数转换为指向函数的指针.

7. 异常与构造&析构函数
1. 构造函数完成对象的构造和初始化,需要保证不要在构造函数中抛出异常,否则可能导致对象不完整或没有完全初始化.
2. 析构函数主要完成资源的清理,需要保证不要在析构函数内抛出异常,否则可能导致资源泄漏(内存泄漏、句柄未关闭等).

二,STL(Standard Template Library, 标准模板库)
了解标准模板库的组成部分:容器(比如,数组,链表等),迭代器(类似于遍历容器的指针),算法,
容器就是用来存储和管理数据的数据结构,STL可以分为三类容器:顺序容器,关联容器,容器适配器

1,顺序容器:
向量,链表,队列

2,迭代器
用来遍历遍历容器的专门类模板

2,关联容器
map // key , value 键,值 对

3,算法

所有容器类的公共操作:

vector<int> ct;
	vector<int> ct1;
	vector<int> ct2;
	默认构造函数				//将对象初始化为空
	带参数构造函数				//除了默认的构造函数之外,每个容器都有带参数的构造函数。
	拷贝构造函数				//当对象作为值参数传递时,及用另一个相同类型的对象初始化对象时执行该函数
	析构函数					//当对象超出作用域时执行该函数
	vt.empty();					//判断容器是否为空。
	ct.size();					//返回容器中当前的元素个数。
	ct.max_size();				//返回可以插入到容器ct中的元素的最大个数
	ct1.swap(ct2);				//交换容器ct1和ct2中的元素。
	ct.begin();					//返回容器中指向每一个元素的迭代器。
	ct.end();					//返回窗口中指向最后一个元素之后的位置,此位置不可引用。
	ct.rbegin();				//倒置开始位置,该函数返回容器ct中最后一个元素的指针,用来倒置处理ct中的元素。
	ct.rend();					//倒置最后位置,该函数返回容器ct中第一个元素的指针。
	ct.insert(position, elem); 	//将elem元素插入到容器ct中由参数position指定位置上。这里的position是迭代器。
	ct.erase(begin, end);		//删除容器ct中从begin到end-1之间的所有元素。
	ct.clear();					//删除容器中的所有元素,在调用此函数后,容器ct为空。
	ct1 = ct2;					//将ct2中的所有元素拷贝到ct1中。
	ct1 == ct2;					//如果容器ct1和容器ct2相等,则返回true,否则 返回false;
	ct1 != ct2;					//如果容器ct1和容器ct2不相等,则返回true,否则 返回false;
	ct1 < ct2;					//如果容器ct1小于容器ct2,返回true,否则返回false;
	ct1 <= ct2;					//如果容器ct1小于或者等于容器ct2,返回true,否则返回false;
	ct1 > ct2;					//如果容器ct1大于容器ct2,返回true,否则返回false;
	ct1 >= ct2;					//如果容器ct1大于或者等于容器ct2,返回true,否则返回false;
顺序容器的公共操作:
	insert(position , elem);		//将elem持拷贝插入到由position指定的位置上,该函数返回新元素的位置
	insert(position, n , elem);		//将n个elem元素插入到由position指定的位置上。
	insert(position, beg, end)		//将从beg到end-1的所有 元素的拷贝插入到由position指定的位置上。
	push_back(elem)					//将elem元素的拷贝插入到容器的末尾
	pop_back						//删除最后一个元素
	erase(position)					//删除由position指定的元素
	erase(beg, end)					//删除从beg到end-1之间的所有元素
	clear							//删除容器中的所有元素
	resize(num)						//将容器中的元素个数改为num,如果size增加,新元素由默认的构造函数创建。
	front()							//返回容器中的第一个元素
	back()							 //返回容器中的最后一个元素
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值