C++类 学习笔记

类和对象的介绍

类,对象,成员函数和数据成员

  • 以关键字class开始类的定义,class后面跟类名,按照惯例,用户定义的类的名字以大写字母开头每个类的体(body)包围在一对花括号中({和})
  • 类的定义以分号结束成员访问说明符public(意为共有的或公共的),成员函数出现在访问说明符public后,表明该函数是公共可用的,也就是说该函数可以被程序中的其他函数或其他类的成员函数所调用,成员访问说明符之后要加冒号(:)
    类定义的格式: class 类名
    {
    private:私有数据成员和成员函数
    protected:保护数据成员和成员函数
    public:公有数据成员和成员函数
    };

数据成员,设置函数和获取函数

  • 声明在一个函数定义的体中的变量为局部变量,只能在函数声明它们的行到函数定义结束的右花括号(})之间使用
  • 类成员有三种不同的访问权限:私有(private) 保护(protected) 公有(public)
成员访问说明符public、private和protected
  • 在成员访问说明符private之后声明的变量或者函数,只可以被声明它们的类的成员函数及其友元所访问
  • 类成员的默认成员访问说明符是private,因此类头部之后,第一个成员访问说明符之前的所有成员都是私有的
  • private声明数据成员被视为数据隐藏
  • 保护成员一般情况下与私有成员的含义相同,在类的继承和派生中与私有成员有不同的含义,保护成员可被本类或者派生类的成员函数访问,但不能被外部函数访问
  • 公有成员可以被程序中的任何函数访问,它提供了外部程序与类的接口功能

类的深入刨析(I)

类的作用域和类成员的访问

简介

  • 在头文件中使用“预处理器封套”,从而防止将头文件中的代码多次包含到同一个源代码文件中
    #ifndef
    #endif
    #define

    预处理器指令#ifndef(如果没有定义)和#endif用来阻止重复包含一个头文件。如果在这些指令之间的代码先前没有包含到应用程序中,那么#define定义一个可用于阻止将来包含的名称,并将此代码包含到源代码文件中。
  • 三种“句柄”——对象名称,对象的引用或者对象的指针来访问类的public成员
  1. 对象的名称或者对象的引用要结合圆点成员选择运算符(.)来访问类的public成员
  2. 对象的指针则要结合箭头成员选择运算符(->)
  • 每个成员访问说明符在类的定义中只使用一次
  • 类的数据成员不能在类体中声明时被初始化(除了整型或枚举类型的类的static const数据成员)
    使用类的构造函数初始化这些数据成员
  • 参数化的流操纵元setfill用于指定当输出域宽大于输出整数值中数字个数时所需显示的填充字符
  • 默认情况下,填充字符出现在数中数字的左边
  • 如果将要输出的数填满了指定的域宽,那么不显示填充字符
  • 一旦用setfill指定了填充字符,该字符将应用在后续值的显示中(也就是说setfill是一个粘性设置)
  • setw是一个非粘性设置,它只对紧接着显示的值起作用

构造函数

  • 构造函数是一种特殊的成员函数,被声明为公有成员,其作用是为类的对象分配内存空间,进行初始化
关于构造函数的说明
  • 构造函数的名字必须与类的名字相同
  • 构造函数没有返回值,不能定义返回值类型,包括void型在内也不可以
  • 对象定义时,编译系统会自动地调用构造函数完成对象内存空间的分配和初始化工作
  • 构造函数是类的成员函数,具有一般成员函数的所有性质,可访问类的所有成员,可以是内联函数,可带有参数,可带有默认的形参值,还可重载
  • 如果没有定义构造函数,编译系统就自动生成一个缺省的构造函数,这个缺省的构造函数不带任何参数,仅给对象开辟存储空间。系统自动生成的构造函数的形式为: 类名::类名(){ }

析构函数

  • 析构函数也是一种特殊的成员函数,也被声明为公有成员,其作用是释放分配给对象的内存空间
关于析构函数的说明
  • 类的析构函数的名字是在类名之前添加发音字符(~)后形成的 字符序列
  • ~类名发音字符是按位取补运算符
  • 析构函数不接受任何参数,也不返回任何值,析构函数不可以指定返回类型,甚至void也不行。
  • 一个类只能有一个析构函数,而且析构函数不允许重载
  • 当对象撤销时,系统会自动调用析构函数完成内存空间的释放和善后工作
  • 如果没有定义析构函数,系统会自动生成一个缺省的空析构函数,其形式为: 类名::~类名(){ }

拷贝构造函数

  • 拷贝构造函数是一个特殊的构造函数,其作用是用一个已经存在的对象初始化本类的新对象。每个类都有一个拷贝构造函数
  • 拷贝构造函数名与类名相同,但参数是本类对象的引用,拷贝构造函数没有返回值
  • 定义拷贝构造函数的格式为: 类名(类名&对象名){ } 其中,对象名是用来初始化另一个对象的对象的引用
  • 构造函数只在对象被创建时自动调用,而拷贝构造函数在下列三种情况下会被自动调用:
    1.用一个对象去初始化本类的另一个对象时
    2.函数的形参是类的对象,在进行形参和实参的结合时
    3.函数的返回值是类的对象,函数执行完返回时

何时调用构造函数和析构函数

  • 对于构造函数和析构函数常见用法是在构造函数中用new动态申请空间,在析构函数中用delete释放内存空间
  • 编译器隐式地调用构造函数和析构函数
  • 当程序执行到自动局部对象的定义处时,该对象的构造函数被调用;当程序执行离开对象的作用域时(也就是说,对象定义其中的块已经执行完毕),相应的析构函数会被调用。当程序的执行每次进入或者离开自动对象的作用域时,自动对象的构造函数或者析构函数就会被调用,如果程序的终止是由调用exit函数或者abort函数而完成的话,那么自动对象的析构函数将不被调用。
  • static局部对象的析构函数只调用一次,即在程序第一次执行到该对象的定义处时;而相应的析构函数的调用则发生在main函数结束或者程序调用exit函数时
  • 对象的引用就是该对象名称的别名(因此它可以在赋值语句的左边使用)

默认的逐个成员赋值

  • 赋值运算符(=)可以将一个对象赋值给另一个类型相同的对象,赋值运算符右边的对象的每个数据成员逐一赋值给赋值运算符左边对象中的同一数据成员

类的深入刨析(II)

const对象和const成员函数

  • 对于const对象,不允许进行成员函数的调用,除非成员函数本身也声明为const(常对象只能调用常函数)
  • 对对象进行的三种其他的成员函数的调用
    对非const对象调用非const成员函数;对非const对象调用const成员函数;对const对象调用const成员函数(即非常对象既可以调用非常成员函数,也可以调用常成员函数,但常对象只能调用常成员函数)
  • 将函数指定为const的,既要在其原型中指定,又要在定义中指定如:
int gethour()const
  • 不允许对构造函数和析构函数进行const声明
    尽管构造函数必须是非const函数,但它仍然可以用来初始化const对象
  • const对象的方法-const加类名加对象名(如const Time noon)
  • 用成员初始化器来初始化const数据成员
    所有的数据成员均可以用成员初始化器形式进行初始化,但是const数据成员和引用的数据成员必须使用初始化器进行初始化
    成员初始化器出现在构造函数的参数列表和作为构造函数体开始的左花括号之间,成员初始化器列表用一个冒号(:)与参数列表分开,每一个成员初始化器都是由数据成员名称后面紧跟着包含该成员初始化值的一对圆括号组成。
    如:
 Increment::Increment(int c,int i):count(c),increment(i)

组成:对象作为类的成员

  • 当用一个类的对象作为 另一个类的成员时,该成员成为对象成员。
  • 声明对象成员的一般格式: class 类名 { 类名1 对象成员1; };
  • 对象成员的初始化,在类中有对象成员时,创建本类的对象则本类的构造函数要调用其对象成员所在类的构造函数,并采用成员初始化列表对对象成员进行初始化。
  • 这种类的构造函数的定义格式为
    类名::类名(参数总表):对象成员1(形参),… ,对象成员n(形参表){ //构造函数体 }
  • 说明:对象成员的构造函数的调用顺序由对象成员在类中的声明顺序决定,与成员初始化列表中的顺序无关。析构函数的调用顺序正好与构造函数的调用顺序相反

friend函数和friend类

  • 类的friend函数在类的作用域以外定义,却具有访问类的非public(以及public)成员的权限。单独的函数或整个类都可以声明为另一个类的友元
  • 使用friend函数可以提高程序的性能
  • private,protected,public这些成员访问说明符标志与友元的声明无关,因此友元定义可以放在类定义内的任何地方(一般放在最前面)
  • 在类定义中函数原型前加保留字friend,就将该函数声明为该类的友元,
    例如:要将类ClassTwo的所有成员函数声明为ClassOne类的友元,应在ClassOne定义中加入如下形式的一条声明:
friend class ClassTwo
  • 良好的编程习惯:在类定义体之内把所有的友元关系声明放在最前面的位置,并且不要在其前面添加任何成员访问说明符
  • 友元关系是授予的,而不是索取的(即对于类B成为类A的友元,类A必须显式地声明类B是它的友元)
  • 友元关系既不是对称的也不是传递的(即如果类A是类B的友元,类B是类C的友元,那么就不能推断类B是类A的友元,类C是类B的友元或者类A是类C的友元)
  • 可以指定重载函数为类的友元,每个打算成为友元的重载函数必须在类的定义里显式地声明为类的一个友元

使用this指针

  • 每个对象都可以使用一个称为this的指针来访问自己的地址
  • 对象的this指针不是对象本身的一部分(即this指针占用的内存大小不会反映在对对象进行sizeof运算得到的结果,相反,this指针作为一个隐式的参数传递给对象的每一个非static成员函数)
  • this指针的类型取决于对象的类型以及使用this的成员函数是否被声明为const
    例如:
    在Employee类的非const成员函数中,this指针具有的类型是Employee*const(一个指向非const Employee对象的const指针)而在Employee类的const成员函数中,this指针具有的类型却为const Employee *const(指向一个const Employee 对象的const指针)
  • 隐式和显式使用this指针来访问对象的数据成员
    例:
void Test::print() const
 { 
  cout<<"x="<<x;  
  cout<<"\n  this->x="<<this->x;
  cout<<"\n  (*this).x="<<(*this).x;
 }

隐式地使用this指针打印x,仅仅指明该数据成员的名称,然后print使用两种不同的表示法通过this指针访问x,一种是箭头运算符(->)紧跟着this指针,另一种是圆点运算符(.)紧跟着间接引用的this指针,当* this与圆点成员选择运算符(.)一起使用时,括住*this的圆括号是必须的,因为圆点运算符具有比 * 运算符更高的优先级,如果不使用这对圆括号,表达式 *this.x将被认为与 *(this.x)是相同的,进行编译时会报错,因为圆点运算符不能与指针一起使用

  • 使用this指针使串联的函数调用成为可能,也就是多个函数在同一条语句中被调用,如:
t.setHour(18).setMinute(30).setSecond(22);
  • 将* this作为一个引用返回的方法就可以支持串联函数的成员函数的调用

使用new和delete运算符进行内存的动态管理

  • 使用new和delete运算符动态分配内存以存放对象,基本类型和数组的具体方式
  • 如以下声明和语句:
 Time * timeptr;
 timeptr=new Time;

上面的new运算符为Time类型的对象分配大小合适的内存空间,调用默认的构造函数来初始化这个对象并返回一个指向new运算符右边类型的指针(也就是Time*)。注意:new可用于动态分配任何基本类型或者类类型

  • 要撤销一个动态分配的对象并且释放这个对象占用的空间,应以如下方式使用delete运算符:
    delete timeptr;
    这条语句首先调用timeptr所指对象的析构函数,然后收回对象占用的内存空间
  • 当动态分配的内存空间不再使用时若不释放,将导致系统过早地用完内存。这有时称为“内存泄漏”。
  • C++允许为新建立的基本类型变量提供初始化值,如:
    double * ptr=new double(3.14159);
    这条语句将新建的double对象初始化为3.14159,并将结果指针赋给ptr。
  • 将由逗号分割开的参数列表指定给对象的构造函数。如:
    Time * timeptr=new Time(12,45,0);
    #将一个新建的Time对象初始化为12:45PM,并把结果赋给timeptr.
  • new运算符可用于动态地分配数组。如:
    int * gradesArray=new int[10];
    分配一个10个元素的整型数组并把这个数组指派给gradesArray;上面的语句声明了指针gradeArray,并且将指向一个动态分配的10元素整数数组第一个元素的指针赋给它,
    当动态分配一个数组时,无法将参数传递给每个对象的构造函数,每个在数组里的对象由自身的默认构造函数初始化
    要删除由gradesArray指向的动态分配的数组,必须使用以下语句:
    delete [] gradesArray;
    这条语句收回gradesArray所指向的数组。如果上面语句中的指针指向一个对象数组,那么语句首先调用数组中每个对象的析构函数,然后再收回空间。
    对于对象数组,如果使用的是delete而不是delete[],将导致运行时发生编译错误。为了确保数组中的每个对象都接受一个析构函数调用,总是使用delete[]运算符将分配给数组的内存空间删除。
    类似地,总是使用delete运算符将分配给单个元素的内存空间删除。

static类成员

  • 对于类的每一个对象来说,它们都各自拥有类所有数据成员的一份拷贝。static数据成员将仅有变量的一份拷贝供类的所有对象共享
  • static成员可以被声明为public,private,protected
  • 尽管类的static数据成员看上去像是全局变量,但他们只在类的作用域起作用
  • 基本类型的static数据成员默认情况下将初始化为0
  • static数据成员可以被初始化一次(且只能是一次)
  • int或枚举类型的const static数据成员可以在类定义中声明处初始化,但是所有其他static数据成员必须在文件作用域(换言之,在类定义体以外)进行定义,并且只能在那些定义中初始化
  • 注意类类型的static数据成员(即static成员对象)如果这个类类型具有默认构造函数,那么这样的数据成员无需初始化,因为它们的默认构造函数将会被调用
  • 类的static成员即使在没有任何类的对象存在时,仍然存在。当没有类的对象存在时,要访问类的public static 成员,只需在此数据成员名前加类名和二元作用域分辨运算符(::)
  • 类的任何public static成员也可以通过任何该类的对象来访问,具体方法是使用对象名,圆点运算符和成员名称
  • 当没有类的对象存在而要访问private或protected的static类成员时,应提供public static 成员函数,并通过在函数名前加类名和二元作用域分辨运算符的方式调用此类函数
  • static成员函数不具有this指针,因为static数据成员和static成员函数独立于类的任何对象而存在。this指针必须指向类的具体的对象

运算符重载字符串和数组对象

运算符重载的基础知识

要在类的对象上使用运算符,该运算符必须重载,但是也有三个例外。所有的类都可以用赋值运算符(=)对其数据成员进行逐个成员赋值操作———赋值运算将“源”对象的每个数据成员赋值给“目标”对象的数据成员。同样,取地址(&)和逗号(,)运算符也可以未经重载就用于任何类的对象。取地址运算符返回对象在内存中的地址。逗号运算符从其左侧的表达式开始求值至右侧表达式。这两个运算符也都可以重载。

运算符重载的限制
  • c++可以重载的运算符:+ - * / % ^ & | ~ ! = < > += -= = /= %= ^= &= |= << >> >>= <<= == != <= >= && || ++ – -> , -> [] () new delete new[] delete[]
  • C++不可重载的运算符: . .* :: ? :
  • 重载不能改变运算符的优先级,结合律和操作数个数(运算符的元数)
  • C++不能创建新的运算符,只有现有的运算符才可以重载
  • 运算符重载不能改变运算符对于基本类型对象操作的含义。例如:程序员不能改变+运算符用于两个整数相加时的含义
作为类成员函数和全局函数的运算符比较

运算符函数可以是成员函数或全局函数。出于性能方面的考虑,全局函数通常指定为友元函数。成员函数用this指针隐式地获得类对象的某个参数(对二元运算符来说即左操作数),而二元运算符的两个操作数参数在全局函数调用中必须显式列出

  • 必须作为成员函数进行重载的运算符
    如果要重载 () [] ->或者任何赋值运算符,运算符重载函数必须声明为类的成员函数。对于其他运算符,运算符重载函数可以是类成员函数或者全局函数
  • 运算符函数作为成员函数实现时:最左边的操作数必须是运算符的一个类对象(或者是对该类对象的一个引用)。如果左操作数必须是一个不同类的对象或者是一个基本类型对象,那么该运算符函数必须作为全局函数来实现。如果全局运算符函数必须直接访问类的private或protected成员,那么该函数可以指定成该类的友元函数
  • 一个特定类的运算符成员函数仅在下面两种情形下(由编译器隐式)调用:当二元运算符的左操作数的确是该类的对象时,或者当一元运算符唯一的操作数是该类的对象时。
重载流插入运算符和流提取运算符
  • 重载的流插入运算符(<<)用在如cout<<classObject之类的表达式中,其左操作数的类型为ostream &.如果要在右操作数是用户自定义类的对象的情况下使用该运算符的话,那么该运算符必须重载成全局函数。要以成员函数重载,运算符<<必须成为ostream类的成员。用户自定义的类不可能做到这一点,因为我们不能修改C++标准库中的类。重载流 提取运算符(>>)用在如cin>>classObject之类的表达式中,其左操作数的类型为istream &,其右操作数是用户自定义类的对象,因此该运算符重载函数也必须是全局函数。此外,这两个重载运算符函数都可能需要访问输出或输入的类的对象的private数据成员,因此出于性能原因,可以把这些重载运算符函数指定为类的友元函数
  • 我们可能选择全局函数来重载运算符的另一个原因是使运算符具有可交换性
  • 流提取运算符>>
  • 流插入运算符<<
重载一元运算符
  • 类的一元运算符可以重载为不带参数的非static成员函数或者带有一个参数的全局函数
  • 全局函数的参数必须是该类的对象或者是该类对象的引用
    实现重载运算符的成员函数必须是非static的,这样它们可以访问该类每个对象的非static数据(static成员函数只能访问类的static数据成员)
重载二元运算符
  • 二元运算符可以重载为带有一个参数的非static成员函数,或者两个参数(其中一个必须是类的对象或者类对象的引用)的全局函数

继承

慨念
  • 继承是软件复用的一种形式,它是从现有类的基础上建立新类,新类继承了现有类的属性和方法,并且还拥有其特定属性和方法。继承的过程称为派生,新建的类为派生类(或子类),原有的类称为基类(或父类)
  • 继承可分为:单继承和多重继承。若派生类只有一个基类则称为单继承;若派生类有多个基类则称为多重继承
  • 继承的目的:实现代码的重用
派生类的声明
  • 单继承中派生类的定义格式为
    class <派生类名>:<派生方式><基类名>
    {
    派生类新增的成员声明
    };
  • 派生方式关键字为public、private和protected
    缺省的继承方式是私有继承。
  • 继承方式规定了派生类成员和类外对象访问基类成员的权限
  • 派生类新增的成员是指继承过程中新增的成员函数和数据成员,通过在派生类中新增加成员实现功能的扩充
派生类的访问权限
  • 公有继承(public)
    继承后基类的公有成员,私有成员和保护成员在派生类中访问权限保持不变
    在派生类中可以直接访问基类的公有成员和保护成员,但对于私有成员的访问只能通过基类的非私有成员函数间接访问
    在基类和派生类定义以外只能通过派生类的对象访问基类的公有成员,无法通过派生类对象直接访问基类的私有成员和保护成员
  • 私有继承(private)
    继承后基类的所有成员在派生类中均为私有成员
    在派生类中可以直接访问基类的公有成员和保护成员,但对于私有成员的访问#只能通过基类的非私有成员函数间接访问
    在基类和派生类定义以外对基类的所有成员均无法直接访问,也无法通过派生类的对象间接访问
  • 保护继承(protected)
    继承后基类的公有成员和保护成员在派生类中均为保护成员,基类的私有成员在派生类中仍为私有成员
    在派生类中可以直接访问基类的公有成员和保护成员,但是对于私有成员 的访问只能通过基类的非私有成员函数间接访问
    在基类和派生类定义以外对基类的所有成员均无法直接访问,也无法通过派生类的对象间接访问
派生类构造函数和析构函数的定义及使用
  • 在派生过程中,构造函数和析构函数不被继承
  • 在创建一个派生类对象时,分别调用基类和派生类的构造函数完成各自成员的初始化工作,当撤销一个派生类对象时,分别调用基类和派生类的析构函数完成善后处理工作
  • 在C++中对构造函数与析构函数的调用顺序有如下规定:1.对于构造函数先执行基类的,再执行对象成员的,最后执行派生类的。2.对于析构函数,先执行派生类的,再执行对象成员的,最后执行基类的
  • 派生类构造函数定义格式为
    <派生类名>::<派生类名>(参数总表):基类#名 (参数总表) ,对象成员名1(参数表1), …,对象成员名n (参数表n)
    {
    //派生类新增成员成员的初始化语句
    }
    说明:1.派生类的构造函数与派生类名相同 2.参数总表列出初始化基类成员数据、新增对象成员数据和派生类新增数据成员所需要的全部参数 3.冒号后列出需要使用参数进行初始化的基类的名字和对象成员的名字及各自的参数表,之间用逗号分开。对于使用缺省构造函数的基类或对象成员,可以不给出类名或对象名以及参数。 4.如果基类没有定义构造函数,派生类也可以不定义构造函数,全都采用缺省的构造函数。如果基类定义了带有形参表的构造函数,派生类就必须定义构造函数,保证在基类进行初始化时能获得所需的数据 5.如果派生类的基类也是派生类,则每个派生类只需要负责其直接基类的构造,不负责间接基类的构造
虚基类的作用、定义和使用
  • 多重继承中的二义性问题
    问题的产生
    1.当派生类继承的多个基类中存在同名函数时,派生类中就会出现来自不同基类的同名成员,就出现了标识符不唯一或者二义性的情况,这在程序中是不允许的
    2.当一个类从多个基类派生而来,这多个基类又有共同的基类,则在派生类中访问这个共同基类中的成员时会产生二义性
    解决办法对于第一种情况:
    1.使用作用域运算符::
    2.使用同名覆盖原则
    3.使用虚函数
    对于第二种使用虚基类
  • 虚基类的定义
    虚基类的声明是在派生类的声明过程中进行的,格式为:
    class<派生类名>:virtual<派生方式><基类名>
    说明1.虚基类关键字的作用范围和派生方式与一般派生类一样,只对紧跟其后的基类起作用
    2.声明了虚基类以后,虚基类的成员在进一步派生过程中和派生类一起维护同一个内存拷贝
  • 虚基类的构造函数和初始化
    虚基类的初始化和一般的多重继承的初始化在语法上是一样的,但构造函数的执行顺序不一样
    1.虚基类的构造函数在非虚基类的构造函数之前执行
    2.若同一层次中包含多个虚基类,这些虚基类的构造函数按它们说明的先后次序执行
    3.若虚基类由非虚基类派生而来,则仍然先执行基类的构造函数,再执行派生类的构造函数

多态性

多态性的概念
  • 多态性是指不同对象接受到相同消息时,根据对象类的不同产生不同的动作
  • 由静态联编支持的多态性称为编译时的多态性或静态多态性,即确定同名操作的具体操作对象的过程是在编译过程中完成的。C++用函数的重载和运算符的重载来实现编译时的多态性
  • 由动态联编支持的多态性称为运行时的多态性或动态多态性,即确定同名操作的具体操作对象的过程是在运行过程中完成的。C++使用继承和虚函数来实现运行时的多态性
类继承层次中对象之间的关系
  • 基类和派生类对象、指针之间允许的赋值操作
    1.基类指针指向基类对象
    2.派生类指针指向派生类对象
    3.基类指针指向派生类对象这是一种安全的方式,因为一个派生类对象也是基类对象,然而这一指针只能用来调用基类的成员函数,如果试图利用该基类指针调用仅派生类含有的成员,编译器将会产生错误。为了避免产生这一错误,必须把基类指针强制类型转换为派生类指针,之后这个派生类指针可以用来调用派生类对象所有的功能
    注:不允许派生类指针指向基类对象,一个基类对象并不具有仅派生类才含有的成员,也就不能通过基类指针调用它们
虚函数的定义使用
  • 虚函数是重载的另一种形式,实现的是动态的重载,即函数调用与函数体之间的联系是在运行时才建立,也就是动态联编
  • 虚函数的定义是在基类中进行的,即把基类中需要定义为虚函数的成员函数声明为virtual。当基类中的某个成员函数被声明为虚函数后,它就可以在派生类中重新定义,其函数原型,包括返回值类型、函数名、参数个数和类型、参数的顺序都必须与基类中的原型完全一致
  • 虚函数定义的一般形式:virtual<函数类型><函数名>(参数表){函数体}
  • 使用虚函数时应注意如下问题
    1.虚函数的声明只能出现在类声明的函数原型的声明中,不能出现在函数体实现的时候,而且,基类中只有保护成员或公有成员才能被声明为虚函数
    2.在派生类中重新定义虚函数时,关键字virtual可以写也可以不写,但在容易引起混乱时,应写上该关键字
    3.动态联编只能通过成员函数来调用或通过指针、引用来访问虚函数,如果用对象名的形式来访问虚函数,将采用静态联编
    4.虚函数必须是所在类的成员函数,不能是友元函数或静态成员函数。但可以在另一个类中被声明为友元函数
    5.构造函数不能声明为虚函数,析构函数可以声明为虚函数
    6.由于内联函数不能在运行中动态确定其外治,所以它不能声明为虚函数
纯虚函数和抽象类
  • 抽象类是一种特殊的类,它为一族类提供统一的操作界面,建立抽象类就是为了通过它多态地使用其中的成员函数。
  • 抽象类是带有纯虚函数的类
  • 一个抽象类至少带有一个纯虚函数。纯虚函数是在一个基类中说明的虚函数,它在该基类中没有具体的操作内容,要求各派生类在重新定义时根据自己的需要定义实际的操作内容
  • 纯虚函数的一般定义格式: virtual<函数类型><函数名>(参数列表)=0;
  • 纯虚函数与普通虚函数的定义不同在于书写形式上加了"=0",说明在基类中不用定义该函数的函数体,它的函数体由派生类定义
  • 如果一个类中至少有一个纯虚函数,这个类就成为抽象类。它的主要作用是为一个族类提供统一的公共接口,以有效地发挥多态的特性。
  • 抽象类的使用应注意的问题
    1.抽象类只能用作其他类的基类,不能建立抽象类的对象。因为它的纯虚函数没有定义功能
    2.抽象类不能用作参数类型、函数的返回类型或显式转换的类型
    3.可以声明抽象类的指针和引用,通过它们,可以指向并访问派生类对象,从而访问派生类的成员
    4.若抽象类的派生类中没有给出所有纯虚函数的函数体,这个派生类仍是一个抽象类,若抽象类的派生类中给出了所有纯虚函数的函数体,这个派生类再是一个抽象类,可以声明自己的对象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值