C++学习笔记(二)

来源:互联网 发布:东莞软件开发公司 编辑:程序博客网 时间:2024/06/07 04:09

友元

 

友元包括友元函数和友元类。

友元可以访问其有好友关系的类中的私有成员。

 

将一个普通函数在类体中通过friend关键字经行声明,就是将该函数声明为该类的友元函数。

 

在通过友元函数访问私有数据成员时,必须加上对象名。所以,友元函数一般都会有一个该类的引用变量,这个变量用于接收传来的对象。

 

友元函数不仅可以是一般函数(非成员函数),而且可以是另一个类中的成员函数。在这里,就用到了提前引用声明。由于友元函数都会有另一个类的引用变量,所以,要对这另一个类经行提前引用声明,声明这是一个类,但并没有对这个类做正事声明,所以用该类的名字去定义的变量只能是指针变量和对象的引用。

 

友元类

 

在A类的定义体中声明B类为其友元类:friend B,则这时,B类的所有函数都是A类的友元函数,可以访问A类中的所有成员。

 

关于友元,有两点特别注意:

1。友元的关系是单向的而不是双向的,B是A的友元,不代表A是B的友元。

2。友元关系不能传递。

 

运算符重载的方法

 

运算法重载的方法是定义一个重载运算符的函数。运算符的重载实际上是函数的重载。

函数类型 operator 运算符名称(形参表列)构成一个函数名

 

运算符被重载后,其原有的功能仍然被保留,没有丧失或改变。而编译器是更具表达式上下文决定的。即根据运算符两侧的数据类型决定的。

 

重载运算符的规则

 

1。C++不允许用户自己定义新的运算符,只能对已有的C++运算符经行重载。

2。不能重载的运算符有5个:.(成员访问运算符).*(成员指针访问运算符)::(域运算符)sizeof(长度运算符)?:(条件运算符)

3。重载不能改变运算符运算对象(即操作数的个数)

4。重载不能改变运算符的优先级别

5。重载不能改变运算符的结合性

6。重载运算符的函数不能有默认的参数,否则就改变了运算符参数的个数,与前面(3)矛盾

7。重载的运算符必须和用户定义的自定义类型的对象一起使用,其参数至少有一个是类对象(或类对象的引用)

8。用于类对象的运算符一般必须重载,但有两个例外,运算符“=”和“&”不必用户重载。

9。应当使重载运算符的功能类似于该运算符作用于标准类型数据时所实现的功能。

10。运算符重载函数可以是类的成员函数,也可以是类的友元函数,还可以是普通函数(其中,友元函数可以访问到类的私有成员,而普通函数则不能)

 

C++规定,有的运算符(如赋值运算符,下标运算符,函数调用运算符)必须定义为类的成员函数,有的运算符则不能定义为类的成员函数(如流插入“<<”和流提取运算符“>>”,类型转换运算符)由于这些运算符左边不是用户自定义类型对象。

一般情况,将单目运算符重载为成员函数,将双目运算符重载为友元函数。

 

继承与派生

 

面向对象程序设计有4个主要特点:抽象,封装,继承,多态。

class 派生类名 : 【继承方式】 基类名

{

    派生类新增的成员;

}

继承方式包括:public(公用的),private(私有的)和protected(受保护的)继承方式是可选的,如果不写此项,默认的继承方式是private

 

派生类中的成员包括从基类继承过来的成员和自己新增的成员两大部分。

 

在声明派生类时,一般还应该自己定义派生类的构造函数和析构函数,因为构造函数和析构函数是不能从基类继承的。

 

派生类成员的访问属性

 

(1)公用继承

基类的公用成员和保护成员在派生类中保持原有访问属性,其私有成员仍为基类私有

 

派生类对象可以直接调用基类中共有成员。

 

(2)私有继承

基类的共有成员和保护成员在派生类中成了私有成员。其私有成员仍为基类私有

 

用私有继承方式建立的派生类称为私有派生类。其基类称为私有基类

 

私有基类的公用成员和保护成员在私有派生类中的访问属性相当于派生类中的私有成员,即派生类中的成员函数能访问它们,而在派生类外不能访问它们,私有基类的私有成员在派生类中成为不可访问成员,只有基类的成员函数可以引用它们

 

(3)受保护的继承

基类的公用成员和保护成员在派生类中成了保护成员,其私有成员仍为基类私有

 

保护成员的意思是:不能被外界引用,但可以被派生类的成员引用。(可以引用但不能直接修改)

 

受保护的成员不能被类外访问,这点和私有成员类似,可以认为保护成员对类的用户来说是私有的,从类的用户角度来看,保护成员等价于私有成员。但有一点与私有成员不同,保护成员可以被派生类的成员函数引用。

 

如果基类声明了私有成员,那么任何派生类都是不能访问它们的。若希望派生类中能访问它们,应当把他们声明为保护成员。如果在一个类中声明了保护成员,就意味着该类可能要用作基类,在它的派生类中会访问这些成员。

 

保护基类的公用成员和保护成员在派生类中都成了保护成员,其私有成员仍为基类私有

 

综合起来,在派生类中,成员有4种不同的访问属性

1。公用的,派生类内和派生类外都可以访问。

2。受保护的,派生类内可以访问,派生类外不能访问,其下一层的派生类可以访问。

3。私有的,派生类的可以访问,派生类外不能访问。

4。不可访问的。派生类内和派生类外都不能访问

 

派生类的构造函数和析构函数

 

基类的构造函数是不能继承的。因此对基类成员初始化的工作也要由派生类的构造函数承担。解决这一问题的思路是:在执行派生类的构造函数时,调用基类的构造函数。

 

其一般形式为:

派生类构造函数名(总参数表列):基类构造函数名(参数表列)

{派生类中新增数据成员初始化语句}

注意:基类构造函数名后面括号内的参数表列只有参数名而不包括参数类型,因为这里不是定义基类构造函数,而是调用基类构造函数。因此这些参数是实参而不是形参

 

当派生类用自己的构造函数创建对象时。总参数列表中的形参被全部赋值。此后,根据基类型构造函数后的参数表列调用基类型的构造函数。完成派生类对象的初始化。

 

在类中对派生类构造函数做声明时,不包括基类构造函数名及其参数列表。只有在定义函数时才将它列出。

 

调用基类构造函数时的实参是从派生类构造函数的总参数表中得到的。也可以不从这些参数表中得到,而是使用常量定义。

 

对于有子对象的派生类构造函数的一般形式:

派生类构造函数名(总参数表列):基类构造函数名(参数表列),子对象名(参数表列)

{派生类中新增数据成员初始化语句}

执行派生类构造函数的顺序是:

1。调用基类构造函数,对基类数据成员初始化;

2。调用子对象构造函数,对子对象数据成员初始化;

3。在执行派生类构造函数本身,对派生类数据成员初始化。

 

而(总参数列表)和(参数列表)之间的数值传递是通过参数名来完成对应关系的。和参数的前后顺序无关

 

而多级派生类则:不要列出每一层派生类的构造函数,只需写出上一层派生类(即它的直接基类)的构造函数即可

 

如果在基类中没有定义构造函数,或定义了没有参数的构造函数,那么,在定义派生类构造函数时可以不写基类构造函数

 

如果在基类或子对象类型的声明中定义了带参数的构造函数,那么就必须显示地定义派生类构造函数,并在派生类构造函数中写出基类或子对象类型的构造函数及其参数表

 

派生类的析构函数

 

在派生时,派生类是不能继承基类的析构函数的,也需要通过派生类的析构函数去调用基类的析构函数。

 

基类的清理工作仍然由基类的析构函数负责,在执行派生类的析构函数时,系统会自动调用基类的析构函数和子对象的析构函数,对基类和子对象经行清理

 

调用顺序与构造函数正好相反:先执行派生类自己的析构函数,对派生类新增加的成员经行清理,然后调用子对象的析构函数,对子对象经行清理,最后调用基类的析构函数,对基类经行清理

 

多重继承

 

多重继承的派生类的构造函数:

派生类构造函数名(总参数表列):基类1构造函数(参数表列),基类2构造函数(参数表列),基类 3构造函数(参数表列)

{派生类中新增数据成员初始化语句}

 

各基类的排列顺序任意。派生类构造函数的执行顺序同样为:先调用基类的构造函数,在执行派生类构造函数的函数体,调用基类构造函数的顺序是按照声明派生类时基类出现的顺序。

 

不同的成员函数,只有在函数名和参数个数相同,类型相匹配的情况下才发生同名覆盖。如果只有函数名相同而参数不同,不会发生同名覆盖,而属于函数重载。(函数的重载不属于面向对象的技术,在编译系统中,函数的重载是由于编译器将用户自定义的函数名翻译成了不同的有一定规定的函数名,使得每个函数的调用不受影响)

 

 

 

 

关于虚继承,虚函数和多态的知识请参照另一篇博文《多态和虚表》