C++基本知识点(读书笔记)

来源:互联网 发布:c语言中的关键字有哪些 编辑:程序博客网 时间:2024/06/01 08:09
《C++程序设计与应用开发》读书笔记。涵盖C++基本知识,易混淆的点,可用于学习、复习。

1. 类成员函数的实现
    类成员函数的实现可以放在两个地方:类体内,类体外。把成员函数放在类体内,是建议编译器将这些成员函数看做内联函数处理。在类体外实现的成员函数,也可以在函数前面加上inline将其定义成内联函数,当然定义成内联函数的成员函数一般放到头文件中
2. 信息隐藏
    在C++中,可以用访问控制修饰符public(公有)、private(私有)和protected(保护)来描述对类成员的访问控制,以使某些成员在类外部不能被访问到,以达到信息隐藏的效果。C++中默认的访问控制修饰符是private
访问控制修饰符
访问控制作用
public
在程序的任何地方都可以访问类public成员
private
只能在类的内部和友元中访问
protected
只能在本类、派生类和友元中访问
    注意,当private,public,protected单纯的作为一个类中的成员权限设置时:
    private: 只能由该类中的函数、其友元函数访问,不能被任何其他访问,该类的对象也不能访问;
    protected: 可以被该类中的函数、子类的函数、以及其友元函数访问,但不能被该类的对象访问; 
    public: 可以被该类中的函数、子类的函数、其友元函数访问,也可以由该类的对象访问。

    注:友元函数包括两种:设为友元的全局函数,设为友元类中的成员函数。

3. 动态对象
    可以通过new和new[]运算符来从堆中申请对象空间,通常这样创建的对象称为态对象。new用来创建单个动态对象,new[]用来创建动态对象数组。利用delete释放单个动态对象的内存空间,利用delete[]运算符释放动态对象数组的内存空间。
    (1)用new创建单个对象的语法为:类名 *对象指针 = new 类名;
    用delete释放单个对象的语法为:delete 对象指针 ;
    (2)用new[]创建动态对象数组的语法为: 类名 *对象指针 = new 类名[对象个数];
    用delete[]释放对象数组的语法为:delete[] 对象指针 ;
4.对象成员的访问
    一般来说,通常将数据成员设置成private访问控制,对它们的访问通过相应的成员函数来进行。
5. 对象的赋值
    同一个类的不同对象之间可以使用赋值运算符“=”互相赋值。在C++中,两个对象赋值时,默认采用的事成员复制(Member-wise Copy)的形式。即,将“对象2”的每个成员赋值给对应的“对象1”的成员(对象名1 = 对象名2)。
6.构造函数
    构造函数(Constructor)是类的一个特殊的成员函数。它的特点如下:
    (1)构造函数是类的成员函数,在类内声明,可以在类体内或类体外实现;
    (2)构造函数名与类名相同;
    (3) 构造函数无返回值;
    (4)构造函数在创建对象时,自动调用。【创建对象:类名 对象名;类名 对象名(实参列表);】
    创建对象数组时,只能调用对象的无参构造函数。例如:Time t[2];
    数据成员初始化的次序是由它在类说明中的出现顺序决定的。
7.成员初始化列表
    成员初始化列表用来对对象的各种成员进行初始化,该列表在构造函数头和函数体之间。包含成员初始化列表的构造函数的语法为:
        类名 (构造函数形参列表) : 成员初始化列表
        {
            . . .
        }
    其中“成员初始化列表”的语法为:成员名(初始化参数列表),成员名(初始化参数列表),. . .
    每个成员的初始化之间用逗号分隔,在成员初始化列表中进行的是对成员的初始化,而不是赋值。
8.析构函数
    析构函数(Destructor)也是类的一个特殊的成员函数。它有如下特点:
    (1)析构函数名为“~类名”;
    (2)没有参数和返回值;
    (3)析构函数不能重载(因为函数名是固定的,参数也是固定的);
    (4)当对象消亡时,系统回收该对象所占用的空间前,析构函数会被自动调用。
    当一个对象中包含对象成员时,会先调用自己的析构函数函数体,然后自动调用对象成员的析构函数,顺序刚好和它们初始化的顺序相反
9.this指针
    每一个成员函数(static成员函数是个例外)都有一个隐藏的指针类型的形参this,其类型为:类名 * const this;
成员函数中队对象的成员的访问是通过this指针类进行的。例如:
    构造函数A : : A(){m=0;}      将编译成      A : : A(A* const this){ this->m = 0 ;}
    通常在成员函数中访问对象的成员时,“this->”可以省略,编译器会自动加上。但是,若成员函数中定义了和数据成员同名的局部变量时,若要访问数据成员则要加“this->”。
10.const成员
    (1)const数据成员。对于常量数据成员的初始化要使用成员初始化列表。
    (2)const函数成员。在定义一个成员函数时,在函数头后面加上一个const修饰,表示它不改变对象的状态。
    (3)const对象。如果一个对象的所有数据成员在对象的整个生命周期中都不会发生变化,则可以将该对象定义成常量对象。例如:A const a;
             对于常量对象,只能调用其const成员函数,因为const成员函数是不会修改对象的状态的。
            常量对象常常用于对函数的形参说明,当把一个对象传给函数时,为了提高函数参数传递的效率,往往把形参定义为对象的指针或引用,但为了防止函数修改实参对象,可以把形参定义为常量对象指针常量对象引用
11. static成员
    (1)static数据成员。静态数据成员可以在没有任何对象的情况下访问到。静态数据成员在程序启动时就被创建和初始化。
    (2)static成员函数。在静态成员函数中只能访问静态成员。

12.友元
    (1)按照面向对象中封装和隐藏的思想,通常在C++中使用private和protected来控制数据成员的访问,通过public成员函数来访问private成员,以达到对外部隐藏其成员的目的。但是函数调用时有开销的,因此,这样有时会降低对私有成员的访问效率。C++中可以通过友元来提高访问效率。
    (2)在一个类S的说明中,可以说明某个全局变量F、某个类的成员函数M为该类的友元函数,也可以将某个类T说明为类S的友元类。
    (3)友元函数和友元类统称为友元,友元的说明是通过关键词friend来实现的。友元的关系是单向的而不是双向的,友元关系不具有传递性

13.内部类
    在C++的函数体内不能定义另一个函数,但是可以在一个类O的类体内再定义另一类I。称类O为外部类,类I为内部类。对于内部类要注意:
    (1)在外部类外面访问内部类时,需要通过外部类作为前缀,格式如下:
      外部类::内部类
      就像内部类是外部类的静态成员一样,但是不能通过外部类的对象来访问。  
    (2)只有public修饰的内部类才能在外部类的外面访问,即创建内部类的对象等。
    (3)可以再外部类内的任何地方创建内部类的对象,并访问内部的成员,但是对内部类成员的访问受成员访问控制的影响,即只能访问public成员。
    (4)可以将内部类的实现放到内部类的外部。
    (5)在外部类中定义内部类,其实相当于在外部类中定义了一种自定义数据类型。
14.运算符重载
    (1)运算符的重载通过重载运算符对应的函数来完成,这些运算符函数有着特殊的函数名:operator运算符,如加运算符对应的运算符函数名为“operator+”。也就是说,运算符重载的本质就是函数重载。
    (2)重载的运算符函数语法为:
    返回值类型 operator# (形参列表)
    {
        相关操作
    }
    operator为关键字,专门用于重载的运算符函数,#代表要被重载的运算符。例如:
    Complex operator+(const Complex &a, const Complex &b)
    {
        . . .
    }
    该函数可以用来实现对两个Complex对象的加,函数中的a、b分别为“+”左右两边的操作数。
    (3)在C++中,重载运算符可以采用两种方式实现。
        <1>. 将重载的运算符作为自定义类的成员函数;
        <2>. 将运算符函数作为全局函数,即不作为任何类的成员。但是,若运算符函数需要访问操作数对象的私有成员,则必须将运算符函数声明为操作数对象类型的友元。因此,常常将这张方式称为全局(友元)函数形式
    (4)作为成员函数重载运算符
        作为成员函数重载时,运算符函数内的参数个数比一般运算符操作数个数少一个。因为,对于类的非静态成员函数,第一个参数默认为指向该类对象的this指针(例外:++,--作为后置时,有一个参数)。
        <1>双目运算符重载。作为非static成员函数重载双目运算符时,只需要给出运算符右边的操作数,左操作数位隐含的this指针,其函数原型声明为:
            返回值类型 operator#(类型);
            函数定义形式为:
            返回值类型 [类名::]operator#(类型  参数)
             {
                    相关操作
             }  
        <2>单目运算符重载。作为类的非静态成员函数重载单目运算符,一般不需要参数(后置++,--除外),运算符的左操作数由函数隐含this指针提供,即运算符的前置用法。对于后置++、--的重载,隐含的this指针提供的是右操作数,因此要提供一个int型参数和前置++、--区别。
            函数的声明语法为:
                返回值类型 operator#();  
            或者:
                返回值类型 operator#(int);//用来进行++、--的后置重载(这里的int型形参只是一个占位符,在调用时,并无实参传入,仅为区别前后置)
            函数定义形式为:
                返回值类型 [类名::]operator#() {      相关操作...       }
            或者:       
                返回值类型 [类名::]operator#(int) {       相关操作...        }  //用来进行++、--的后置重载
    (5)作为全局(友元)函数重载运算符
        采用全局(友元)函数的形式来实现运算符重载时,要求重载的运算符函数的参数至少有一个为自定义类型(如类、结构、枚举等)或自定义类型的引用类型。因为运算符对基本数据类型或指针类型参数的操作已经由编译器内部定义和支持,程序中不能修改。
        另外,运算符函数为全局函数,没有类似this的参数传入,因此,必须将运算符的所有参数都指定在形参列表中。
    (6)特殊运算符的重载
        <1>赋值运算符“=”。默认情况下,C++编译器会为每个类提供一个类成员函数方式的赋值运算符函数。有时默认的赋值运算符函数并不能满足需要,甚至出现错误的危险,例如,在类的数据成员中包含指向动态分配的内存的指针成员时,在复制成员时就可能出现危险。
        拷贝构造函数和赋值运算符(=)函数调用场合的区别:创建对象时,用一个已存在的同类对象对其进行初始化,则调用拷贝构造函数。对两个已存在的对象,通过赋值运算符类赋值时,则调用赋值运算符函数。
        <2>下标运算符“[ ]”。下标运算符函数不能用全局(友元)函数定义,只能是非静态成员函数。在类中的声明语法为:
            返回值类型 operator[ ] (类型 下标);
            其中“下标“是指下标的位置,”类型“一定要是整型(如short、int、long),不能使浮点类型。一般返回对所访问元素的引用。
        <3>函数调用符"()"。重载函数调用符之后,相应类的对象就可以像函数一样调用。函数调用运算符函数在类中的声明语法为:
                返回值类型 operator()(参数列表);
            使用语法为:   
                类名 obj ;    obj(实参列表);    //像函数一样调用obj的某个功能。
            函数调用重载运算符函数只能作为类的非静态函数类重载。
         <4>类成员访问运算符"->"。类成员访问运算符(->)作为一个双目运算符,其第一个操作数为一个指向类或者结构的指针,第二个参数为第一个指针所指向的类或结构的成员。通过对“->”重载,可以实现一种智能指针(Smart Pointer)。重载的“->”运算符函数返回的是某个类对象的地址。
        <5>自增自减运算符“++、--“  
            重载格式汇总:                                  
运算符名
成员函数形式
全局函数形式
调用形式
等价调用
前置++
T operator++()
T operator++(X&)
++a
a.operator++()
后置++
T operator++(int)
T operator++(X&,int)
a++
a.operator++(0)
前置--
T operator--()
T operator--(X&)
--a
a.operator--()
后置--
T operator--(int)
T operator--(X&,int)
a--
a.operator--(0)
        <6>new 与 delete运算符
            对某个类来讲,可以通过重载该类的运算符new于delete来实现控制在何处为该类的动态对象分配空间,这样可提高堆空间分配和归还效率。并且,这样我们可以尽量避免”碎片“问题。

15.继承与派生
    (1)在定义单继承时,派生类只能有一个直接父类,其定义如下:
        class 派生类名:[继承方式] 基类名
        {
            成员说明表
        }    
        <1>继承方式可以忽略,默认为private。“成员说明表”用于给出在派生类中新增的成员。
        <2>赋值运算符函数不能被派生类继承,若派生类没有定义重载的赋值运算符(=)函数,则对派生类对象的赋值将调用编译器提供的默认的赋值运算符函数。
        <3>派生类中可以给出新的成员,也可以对基类的成员进行重新定义(此时若要使用从基类中继承类的定义,必须用基类名受限,即若要调用被隐藏的成员,必须采用基类名作为前缀进行限制)。
    (2)基类的成员在派生类中的访问权限,是由该成员在基类中的访问控制方式和派生类的继承方式共同决定的。
        一个类的public成员在任何地方都可以被访问,protected成员可以在自身内部以及派生类内部访问,private成员只能在自身内部访问。
    (3)C++允许为一个派生类指定多个基类,这样的继承结构被称为多重继承
        在定义多重继承的派生类时,需要给出两个或两个以上的直接基类。其格式为:
        class 派生类名:[继承方式]基类名,[继承方式] 基类名,. . .
        {
            派生类新增成员说明表
        }
    (4)成员名的二义性。在多重继承中,当多个基类中包含同名的成员时,它们在派生类中就会出现成员名的二义性问题。在C++中,解决的方法是采用基类名访问受限。例如  CA::f();     c.CA::f()  ;

16.静态成员变量
    (1)静态全局变量
<1>该变量在全局数据区分配内存。
<2>初始化:如果不显式初始化,那么将被隐式初始化为0(自动变量是随机的,除非显式地初始化)。
<3>访变量只在本源文件可见,严格的讲应该为定义之处开始到本文件结束。
    (2)静态局部变量
<1>该变量在全局数据区分配内存。
<2>初始化:如果不显式初始化,那么将被隐式初始化为0,以后的函数调用不再进行初始化。
<3>它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或 语句块结束时,其作用域随之结束。
    (3)静态函数
        <1>注意与类的静态成员函数区别 。
        <2>静态函数与普通函数不同,它只能在声明它的文件当中可见,不能被其它文件使用。
        <3>其它文件中可以定义相同名字的函数,不会发生冲突;
    (4)面向对象
        静态数据成员 : 
    <1>在类内数据成员的声明前加上关键字static,该数据成员就是类内的静态数据成员。
    <2>静态数据成员存储在全局数据区。静态数据成员定义时要分配空间,所以不能在类声明中定义。静态成员数据,必须在类体外定义,类体内为声明 而已。
        静态成员函数:
        与普通函数相比,静态成员函数由于不是与任何的对象相联系,因此它不具有this指针。从这个意义上讲,它无法访问属于类对象的非静态数据成        
    员,也无法访问非静态成员函数,它只能调用其余的静态成员函数。
17.重复继承--虚基类
    (1)在多重继承中,如果多个直接基类有公共的基类,则会出现重复继承。
    (2)继承时,在基类前面加virtual关键字就可以实现虚继承。例如:
        class B : virtual public A
        {
            . . .  //类B虚继承A,类A称为类B的虚基类
        };
       对于虚继承,注意:   
        <1>编译器在编译任何一个派生类B时,若B以及B的多个基类(直接基类或间接基类)有共同的虚基类A,则在类B中只会保留一份A的成员。
        <2>若派生类B的基类(直接基类或间接基类)有虚基类,则必须在B的构造函数中调用该虚基类的构造函数,以对唯一的拷贝进行初始化。系统保证这个唯一的拷贝只会被初始化一次。

18. 继承与组合
    继承描述的是派生类和基类之间的"is-a"关系。组合描述的是新类和已有类之间的一种包含的关系(新类 "has-a" 旧类)。

19.子类型
    (1)有一个特定的类型S,当且仅当它至少提供了类型T的行为,则称类型S是类型T的子类型。
    (2)只有public继承可以实现子类型,只有public派生类才是基类真正的子类型,它完整的继承了基类的功能。
    (3)使用基类对象的时候可以使用派生类对象来代替,具体表现在一下几个方面:
        <1>基类指针可以指向派生类的对象。
                parent *pp = new parent;
                pp = new child;   //将派生类对象地址 赋值给 基类对象的指针
        <2>基类的引用类型变量可以引用派生类对象。
                parent p;
                parent &rp = p;
                child d;
                rp = d;    //rp建立对派生类对象的引用
        <3>可以利用派生类对象给基类对象赋值。
                child d;
                d.mp= 200;    
                parent p;
                p.mp = 300;
                p = d;    //p.mp 变为 200
                编译语句 p = d 时,编译器在内部调用了parent的默认赋值运算符函数p.operator=(d); 默认情况下,类的赋值运算符函数是内粗的
        直接拷贝。因此只拷贝d内的匿名基类对象部分到p的存储单元中。

20. 多态性与虚函数
    (1)静态多态性是指在编译期间就可以确定所调用的函数。动态多态性是指在运行时,才能够确定所调用的函数。因此,动态多态性也称为运行时
多态性。运行时多态性是通过继承和虚函数实现的。
    (2)虚函数是非静态的成员函数。其说明格式为:
        virtual 类型说明符 函数名 (形参列表)  
        即在类的说明中,在函数原型前加关键字 virtual
    在运行时,根据 父类指针或引用 实际引用和指向的对象类型(动态类型)来确定所调用的函数属于哪一个类(父类or子类)。
    (3)多态性
        <1>多态性可以简单地概括为“一个接口,多种方法”,程序在运行时才决定调用的函数,它是面向对象编程领域的核心概念。多态(polymorphisn),字面意思多种形状。
   <2>C++多态性是通过虚函数来实现的,虚函数允许子类重新定义成员函数,而子类重新定义父类的做法称为覆盖(override),或者称为重写。重写可以有两种,直接重写成员函数和重写虚函数,只有重写了虚函数的才能算作是体现了C++多态性
  <3>多态与非多态的实质区别就是函数地址是早绑定还是晚绑定。如果函数的调用,在编译器编译期间就可以确定函数的调用地址,并生产代码,是静态的,就是说地址是早绑定的。而如果函数调用的地址不能在编译器期间确定,需要在运行时才确定,这就属于晚绑定。
  <4>那么多态的作用是什么呢,封装可以使得代码模块化,继承可以扩展已存在的代码,他们的目的都是为了代码重用。而多态的目的则是为了接口重用。也就是说,不论传递过来的究竟是那个类的对象,函数都能够通过同一个接口调用到适应各自对象的实现方法。
  <5>最常见的用法就是声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而实现不同的方法。如果没有使用虚函数的话,即没有利用C++多态性,则利用基类指针调用相应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。因为没有多态性,函数调用的地址将是一定的,而固定的地址将始终调用到同一个函数,这就无法实现一个接口,多种方法的目的了。
        <6>重载则是允许有多个同名的函数,而这些函数的参数列表不同,允许参数个数不同,参数类型不同,或者两者都不同。编译器会根据这些函数的不同列表,将同名的函数的名称做修饰,从而生成一些不同名称的预处理函数,来实现同名函数调用时的重载问题。但这并没有体现多态性。

21. C++重载、覆盖、隐藏规则
     (1)成员函数被重载的特征:
        <1>相同的范围(在同一个类中);
        <2>函数名字相同;
        <3>参数不同;
        <4>virtual 关键字可有可无。
    (2)覆盖是指派生类函数覆盖基类函数,特征是:
        <1>不同的范围(分别位于派生类与基类);
        <2>函数名字相同;
        <3>参数相同;
        <4>基类函数必须有virtual 关键字。

    (3)“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
        <1>如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual
关键字,基类的函数将被隐藏(注意别与重载混淆)。
        <2>如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual
关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。

22.C++纯虚函数
     (1)定义
            纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原    
        型后加“=0” 
            virtual void funtion()=0 
        若将某个函数声明为纯虚函数,则表示该函数不需要实现,即没有函数体。
    (2)引入原因
       <1>为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。 
       <2>在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。 
  为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual ReturnType Function()= 0;),则编译器要求在派生类中必须予以重写以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题。
    (3)相似概念
       <1>多态性 
      指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。C++支持两种多态性:编译时多态性,运行时多态性。 
          a、编译时多态性:通过重载函数实现 
          b、运行时多态性:通过虚函数实现。 
      <2>虚函数 
      虚函数是在基类中被声明为virtual,并在派生类中重新定义的成员函数,可实现成员函数的动态覆盖(Override)
      <3>抽象类 
      包含纯虚函数的类称为抽象类。由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象类的对象(可以创建抽象类的指针)。

23.  虚函数的注意点
    对于虚函数有一下几个方面需要注意:
    (1) virtual只用在类定义的虚函数说明中,若虚函数在类体外实现,则不能再前面加virtual。
    (2)virtual具有继承性。若基类中定义了某虚函数,则派生类中同名且同形参列表的函数,无论是否使用virtual进行说明都自动为虚函数
    (3)所有非虚函数的调用都一律采用静态绑定。
    (4)构造函数不能是虚函数。
    (5)析构函数可以(往往)是虚函数。

24.虚函数动态绑定实现
    (1)当创建一个包含有虚函数表的类的对象时,在所创建的内存空间里有一个隐藏的指针(vptr)指向该对象所属的虚函数表
    (2)若p为某个基类的引用或指针变量,则当通过p访问对象的虚函数时,将利用虚函数表类动态绑定实际调用的函数。编译器在编译时添加额外的代码来实现动态绑定,这些代码实现以下功能:
    <1>根据p到指向或引用的对象
    <2>在该对象内存空间的固定位置(这个位置由C++语言的实现来确定)找到vptr,根据vptr的值虚函数入口表
    <3>根据调用的函数名以及参数类型列表到虚表中查找对应的名字和参数类型都相同的函数的入口地址
    <4>跳到对应的函数处执行

25.输入输出流
    C++的输入输出通常采用重载的"<<"(析取器)和">>"(插入器)运算符来完成。
    操纵符也称为控制器函数,是为了简化对I/O流的格式设置和简化流的常用操作而定义的一些inline函数,通过调用这些函数可以设置对应流的输出格式。要在程序中使用操纵符,必须采用"#include <iomaniop>"指令来包含iomaiop头文件。(例如:flush、endl)

26. 强制类型转换
    (1)static_cast
        用法: static_cast < type-id > ( expression )
该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。
    (2)reinterpret_cast
        操作符修改了操作数类型,但仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换。
    (3)C++中static_cast和reinterpret_cast的区别
编译器隐式执行任何类型转换都可由static_cast显式完成 ; reinterpret_cast通常为操作数的位模式提供较低层的重新解释。
所谓“通常为操作数的位模式提供较低层的重新解释“,也就是说将数据以二进制存在形式的重新解释。
    (4)C++中强制类型转换函数有4个
    const_cast(用于去除const属性),static_cast(用于基本类型的强制转换),dynamic_cast(用于多态类型之间的类型转换),reinterpreter_cast(用于不同类型之间的指针之间的转换,最常用的就是不同类型之间函数指针的转换)。

27. C++的异常处理机制
    (1)抛出异常 throw
        throw [表达式];
        表达式值得类型称为异常类型,它可以是任意的C++类型(void除外),包括C++的类。
    (2)捕获和处理异常try-catch
        try 
        {
            . . . //可能抛出异常的语句序列
        }
        catch(异常类型名1 [异常对象名])
        {
            . . . //异常处理代码
        }
        catch(异常类型名n [异常对象名])
        {
            . . .  //异常处理代码
        }
    (3)自定义异常类
        C++中可以抛出自定义类型的异常,因此可以在程序中定义更加符合实际意义的类型作为异常类型。
        在程序中采用自定义类型异常时,可能会出现某个异常类型type从一个基类base派生的情况,则基类的异常处理块将捕获派生异常类型。
        若有基类指针类型和派生类指针类型同时作为异常类型,则通常将处理基类指针类型的catch块放在派生类指针类型的catch块后面,因为基类指针类型的catch块会屏蔽掉派生类指针类型的catch块。    
    (4)异常规范
        C++的异常规范提供了在函数声明中描述该函数可能抛出的异常的方法,异常规范跟随在函数参数列表之后,它使用关键字throw来指定,语法如下:
        throw ([类型列表])
        其中类型列表是一个可选项,其中包括了一个或多个类型的名字,它们之间以逗号分割,表示该函数可能抛出的异常类型。如果函数没有异常规范指定,则表示可能抛出任意类型的异常。
        double f(double a, double b);    //f可能抛出任何异常
        double g(double a, double b) throw(int , string) ; //g可能抛出整型、string异常
        double h(double a, double b) throw();    //h不会抛出异常
28.  泛型机制--模板
    (1)函数模板
    <1>定义。函数模板是指带有类型参数的模板,定义语法如下:
    template <class T1, class T2, . . . >
    返回值类型 函数名(参数列表)
    {
        . . .
    }
    其中,T1、T2等是函数模板的模板参数,在调用该函数时将指定T1、T2对应的实际类型。
    <2>调用。调用通过函数模板定义的函数时,需要提供相应的具体的类型。格式为:
        函数名<类型名1,类型名2,. . .>(实参列表)
    (2)类模板
    <1>定义。格式如下:
    template <class T1, class T2, . . . >
    class 类名
    {
        成员说明
    };
    其中,T1、T2等为类模板的类型参数。
    <2>实例化。类模板实例化所需要的模板参数要在程序中显示指出,例如:
        B<int> b;

主要记录于 2013年7月至2013年8月。
参考资料 《C++程序设计与应用开发》,王继民等编著,清华大学出版社。

By Lankin.

0 0
原创粉丝点击