C++入门基础语法

注意

1、类中的static成员
(1)必须在类中声明,在类外定义、初始化(去掉static);
(2)类中成员有static关键字修饰的成员,是类的共同属性
2、const型成员方法
   const成员函数声明和实现都要有const修饰
3、inline型成员函数
    函数定义体放在一起才能使函数成为内联,仅将inline 放在函数声明前面不起任何作用

第1章 面向对象语言概述

1.1 面向对象的语言

面向对象语言主要是封装(抽象)、继承、多态特性,更直接地描述客观世界中存在的事物(对象)以及它们之间的关系。

1.1.1 特点

.是高级语言。
.将客观事物看作具有属性和行为的对象。
.通过抽象找出同一类。
.对象的共同属性和行为,形成类。
.通过类的继承与多态实现代码重用。

1.1.2 面向对象方法

(1)特点
.将数据及对数据的操作方法封装在一起,作为一个相互依存、不可分离的整体——对象。
.对同类型对象抽象出其共性,形成类。
.类通过一个简单的外部接口,与外界发生关系。
.对象与对象之间通过消息进行通信
(2)优点
.程序模块间的关系更为简单,程序模块的独立性、数据的安全性就有了良好的保障。
.通过继承与多态性,可以大大提高程序的可重用性,使得软件的开发和维护都更为方便。

1.1.3 优缺点

(1)优点
易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护。
(2)缺点
性能比面向过程低

1.2 面向过程语言

1、设计思路
自顶向下、逐步求精。采用模块分解与功能抽象,自顶向下、分而治之。
2、程序结构
.按功能划分为若干个基本模块,形成一个树状结构。
.各模块间的关系尽可能简单,功能上相对独立;每一模块均由顺序、选择和循环三种基本结构组成。
.其模块化实现的具体方法是使用子程序。
3、优点
有效地将一个较复杂的程序系统设计任务分解成许多易于控制和处理的子任务,便于开发和维护。
4、缺点:可重用性差、数据安全性差、难以开发大型软件和图形界面的应用软件

第2章 基础语法

2.1 基本概念

2.1.1 引用的定义:&

1、一般用法
(1)定义:相当于给被引用的变量取别名
a:引用指向某个变量
例如:int a=100; int &b=a;----》&b == &a,b是变量a的别名
b:引用地址
例: char a; char* &b = &a;
(2)功能:主要是作传递函数参数和返回值
(3)C++有三种参数传递方式:值传递、指针传递和引用传递;
引用传递的性质象指针传递,书写形式象值传递(简洁)。
2、特点
(1)引用类型变量必须在声明时立即初始化,不允许空引用引用
(2)一旦初始化,就不能再引用其它数据
(3)引用和被引用的变量实际上代表同一个内存的数据
(4)引用传递的性质象指针传递,书写形式象值传递(简洁),
3、使用注意
(1)将一个引用设置为常量后,不能通过该引用修改数据;但仍可通过被引用的变量来改变
int a = 100;
const int& b = a; // b与a是同一内存,但不能直接修改b
b = 1000; // 错误
a = 1000; // OK
(2)被引用的数据是常量,引用本身也必须定义为常量

2.1.2 类中的内联函数

(1)在类中声明并直接定义的成员函数,会自动成为inline函数使用inline关键字声明
(2)inline函数的优点:调用快速,但要求函数的规模短小
注意:
(1)在类中声明并在类外定义时,也要加上inline修饰
(2)内联函数体中不要有复杂结构(如循环语句和switch语句)。

2.1.3 输入输出

1、重载运算符: ‘<<’、 ‘>>’
2、输出cout
输出操作是向流中插入数据,因此‘<<’称为插入运算符。
(1)使用插入运算符‘<<‘,可以向cout流中插入不同类型的数据(自动识别类型),同一个cout流中可以插入多个数据项
cout<<"my name is "<< name << ",my age is " << age << endl;
(2)格式化输出
setfill(char * ): //设置当前输出填充字符,与setw配合使用
setw(unsigned int )://设置当前输出宽度
cout.setf(ios:格式)://设置输出格式(对齐,进制,浮点等)
3、输入cin
输入操作是从流中提取数据,因此‘>>’称为提取运算符.
(1)使用提取运算符’>>’,可以从cin流中提取不同类型的数据,同一个cin流中可以提取多个数据项,并赋值给其后的多个变量;
(2)关于字符另一种操作
cin.get(): 从键盘提取一个字符
cin.getline(char *line, int size, char=’\n’):提取一行文本,默认遇换行结束

2.1.4 域操作符 ‘::’ 成员运算符‘.’

1、’::‘是作用域操作符,指明属于哪个作用域(名字空间、类等),为编译器指定查找的作用域
.当‘::’前没有跟任何作用域名时,则代表当前作用域,即当前文件全局变量集
2、’.'成员运算符
‘.’是成员运算符,指明属于是哪个对象(结构体对象、类对象)的成员。

2.1.5 堆栈空间

1、申请:new
(1)一般用法
单个:new 类型(初值:会调用类的构造函数)
数组:new 类型[nsize] nsize为类型的个数,即申请多少个该类型的变量空间
strut Student *s = new Student[10] ;
(2)new和多维数组的堆空间申请
相当于与指针与二维数组
2、释放:delete
(1)一般用法
delete [] 指针变量
[]是可选的,但当释放数组所占内存时必须加[]
(2)例如
delete s; //释放(里面成员也申请有堆内存会自动先释放),而free不会需要手动
delete [] s //删除多个
3、内存分配时的出错处理

4、new/delete与malloc/free区别
(1)malloc/free
同一个结构体内,如果成员也申请堆内存,需要先手动释放成员的申请堆空间,在释放结构体的堆空间。
(2)new/delete
直接释放大结构体即可,系统自动释放里面的

2.1.6 String类型

string类型是用来定义或初始化一个字符串的类,具有构造、析构、复制构造函数。
1、string字符相关操作
(1)字符串比较
用==、>、<、>=、<= 、!=来比较字符串
(2)字符串连接
用+、+=连接字符串
str += “welcome.”
(3)使用下标来获取字符(相当于字符数组)
string str = “ssss”; // "ssss"存在只读数据段
str[0] == ‘s’;
(4)赋值
直接用‘=’号
2、string类的方法(类成员)
int length() const; //返回当前字符串长度
int size() const //返回当前字符串大小
bool empty() const; //当前字符串是否为空
int max_size() const //返回字符串可存放最大字符串的长度
void resize(int len, char c); //把字符串当前大小置为len, 空余部分用字符c填充
3、string的查找
size_type find(,);

2.2 类和对象

2.2.1 类和对象基本概念

1、类
.把对象的属性和服务结合成一个独立的系统单元。
.具有相同属性和服务的一组对象的集合
.为属于该类的全部对象提供了抽象的描述,包括属性和行为两个主要部分
.类与对象的关系:对象是类的一个实例
2、封装 :保证数据安全,提高可拓展性
.尽可能隐蔽对象的内部实现细节。对外形成一个边界(或者说一道屏障),
只保留有限的对外接口使之与外部发生联系。
3、继承
.继承对于软件复用有着重要意义,是面向对象技术能够提高软件开发效率的重要原因之一。
.定义:特殊类的对象拥有其一般类的全部属性与服务,称作特殊类对一般类的继承。
4、多态性
多态是指在一般类中定义的属性或行为,被特殊类继承之后,可以具有不同的数据类型或表现出不同的行为。这使得同一个属性或行为在一般类及其各个特殊类中具有不同的语义。

2.2.2 this指针

1、特殊指针:指向不同类对象,以访问各类成员
2、调用格式:this->类成员
3、以下情况要显示使用this指针
(1)在类的非静态成员函数中返回类对象本身的时候,
	 直接使用return *this,例如实现对象的链式引用;
(2)当参数与成员变量名相同时,如this->x = x,不能写成x = x;
(3)避免对同一对象赋值。
4、类中的非静态成员函数,有一个隐含this指针参数

MovePoint (int a, int b)函数的原型应该是 void MovePoint( Point *this, int a, int b)

point1.MovePoint(2,2) <===> point1.MovePoint(&point1, 2,2)

void MovePoint(int a, int b) { this->x +=a; this-> y+= b;}
5、类中静态成员函数没有this指针

2.2.3 类构造函数&析构函数&复制构造函数

注意:
(1)当类有动态内存分配的时候,需要重载复制构造、析构函数,以管理动态内存;其他的默认即可;
(2)构造函数初始化列表:可以节省程序执行的空间、时间(因只调用复制构造函数)
(3)除以下情况外,编译器会为类提供一个public的默认构造函数
A: 在类中声明了任何一个构造函数,编译器不会再提供pubic的默认构造函数;若需要有一个默认构造函数的话,程序员必须自己声明并编写一个默认构造函数。
B: 在类中声明了任何一个复制构造函数,编译器不会再提供pubic的默认复制构造函数;若需要有一个默认复制构造函数的话,程序员必须自己声明并编写一个默认复制构造函数。
(4)如果程序中未声明析构函数,编译器将自动产生一个默认的析构函数

1、构造函数
(1)特点:无类型,名字跟类名一样,参数视初始化类的成员定
默认构造函数无参数
(2)功能:创建类对象时,被自动调用,初始化操作(一些类成员)
(3)允许为内联函数、重载函数、带默认形参值的函数
(4)构造函数初始化列表
1)概念
A:对无参数构造函数:
类名():成员变量名(值),成员变量名(值),…
B:参数构造函数:
类名(值1,值2,值3,…):成员变量名(值1),成员变量名(值2),成员变量名(值3),…
若类为派生类,对本类成员变量初始化的同时,也要对基类成员变量初始化
C:参数构造函数:
类名(值1,值2,值3,…):基类名(值1,值2,…),成员变量名(值3),成员变量名(值4),成员变量名(值5),…
2)多类型构造函数
在构造函数的声明时,其参数赋值默认值即可,但定义时候不要再赋初值,这样就可当多类型的构造函数了。
A:类中声明
在这里插入图片描述
B:类外定义
student::student(string name, double score) { … }
C:调用情况
在这里插入图片描述
3)类默认赋值运算符方法
//1,默认构造函数
defaultClass() { }
//2,默认复制构造函数
defaultClass(defaultClass &df) { *this = df;}
//3,默认析构函数
~defaultClass() { }
//4,默认赋值运算符方法
defaultClass &operator=(defaultClass &df) { ; }
//defaultClass &operator=(defaultClass &df) { *this=df; return *this;}
例如:
A a1(10); // 为a1调用构造函数
A a2; // 为a2调用默认构造函数
a2 = a1; // 为a2调用默认赋值函数。 区别 A a2=a1;
2、析构函数
(1)特点:无类型,名字为类名前加‘~’号,参数视类销毁成员而定
默认析构函数无参数
(2)功能:类对象消亡或程序退出时,会被自动调用,清理销毁操作
3、复制构造函数:
(1)特点:无类型,名字跟类名一样,只有一个参数(类型为:类对象的引用)
(2)功能:创建一个新的对象,该对象是另一个对象的拷贝。
(3)注意:当原对象有指针时,若原对象消亡,那么该指针也被释放,会造成新对象的该指针指向非法空间,所有需要重写复制构造函数,并给新对象的该指针重新申请空间,在复制原来指针的内容。
(4)调用(以下情况,复制构造函数会被调用)
A: 一个对象需要通过另一个对象进行初始化
Student s1;
Student s2(s1); student s3=s1; //创建s2,s3时,都会调用。
B: 一个对象以值传递方式传入函数体(作函数参数)
C: 一个对象以值传递方式从函数返回(作函数返回值)
4、构造函数与析构函数执行顺序
构造函数与析构函数执行顺序相反。成员类对象先声明先构造,与初始化列表无关;先执行父类的构造函数。
3、构造函数初始化列表
(1)本类成员初始
对无参数构造函数:
类名():成员变量名(值),成员变量名(值),…
参数构造函数:
类名(值1,值2,值3,…):成员变量名(值1),成员变量名(值2),成员变量名(值3),…
(2)若类为派生类,对本类成员变量初始化的同时,也要对基类成员变量初始化
参数构造函数:
类名(值1,值2,值3,…):基类名(值1,值2,…),成员变量名(值3),成员变量名(值4),成员变量名(值5),…

2.2.4 作用域

1、函数原型作用域:(声明时)
函数原型中的参数,其作用域始于"(",结束于")"
2、块作用域:{ 块作用域 }
在块中声明的标识符,其作用域自声明处起,限于块中。
3、类作用域
(1)类作用域作用于特定的成员名。
(2)类X的成员M具有类作用域,对M的访问方式如下:
如果在X的成员函数中没有声明同名的局部作用域标识符,那么在该函数内可以访问成员M。
通过表达式x.M或者X::M访问。
通过表达式prt->M
4、文件作用域
不在前述各个作用域中出现的声明,具有文件作用域,这样声明的标识符的作用域开始于声明点,结束于文件尾。
5、作用域与可见性
(1)可见性
可见性是从对标识符的引用的角度来谈的概念
可见性表示从内层作用域向外层作用域“看”时能看见什么
(2)本作用域同名标识符会掩盖外部的同名标识符
. 标识符应声明在先,引用在后。
. 如果某个标识符在外层中声明,且在内层中没有同一标识符的声明,则该标识符在内层可见。
. 对于两个嵌套的作用域,如果在内层作用域内声明了与外层作用域中同名的标识符,则外层作用域的标识符在内层不可见。

2.2.5 类静态(static)成员

(1)静态成员不是对象成员,是该类的所有对象维护该成员的同一个拷贝;因为静态类的静态成员属性整个类(包括派生类),在类实例化对象前就已经被分配了空间,而类的是静态成员是在实例化对象是才分配内存空间。
(2)但也可以通过类名的对象来访问静态成员和静态函数
(3)static成员没有this指针

1、静态数据成员
功能:静态成员可以实现多个对象间的数据共享,并且使用静态数据成员不会破坏隐藏规则,保证数据安全;
特点:.用关键字static声明
.该类的所有对象维护该成员的同一个拷贝,其只存储在一处(存储在数据段),为所有对象共有
.必须在类外定义和初始化,用’类名::‘来指明所属的类,因为静态数据成员是静态存储的,并且去掉static。

2、静态成员函数
特点:不是对象成员,静态成员可以实现多个对象间的数据共享。
.可以在类内定义
.类外代码可以使用类名和作用域操作符来调用静态成员函数。
.静态成员函数只能直接引用属于该类的静态数据成员或静态成员函数。

2.2.6 类只读(const)成员

注意:
(1)const型的成员方法只能调用其它的const型的成员方法(静态成员函数除外)
(2)const型的对象,只能调用const型成员方法
(3)const型的成员方法,不能对任何成员数据使用赋值操作符=

1、常类型
常类型的对象必须进行初始化,而且不能被更新。
常引用:被引用的对象不能被更新。
const 类型说明符 &引用名
常对象:定义时必须进行初始化,不能被更新。
类名 const 对象名(实参列表)
常数组:数组元素不能被更新。
类型说明符 const 数组名[大小]…
常指针:指向常量的指针

2、用const修饰的类成员
(1)常成员函数
. 使用const关键字说明的函数。
. 常成员函数不能更新对象的数据成员,也不能调用非const修饰的成员函数(静态成员函数除外)
. 常成员函数说明格式:
类型说明符 函数名(参数表)const;
这里,const是函数类型的一个组成部分,实现部分也要带const关键字。
. const关键字可以被用于参与对重载函数的区分
(2)常对象
.定义必须进行初始化,不能被更新。
.格式:类名 const 对象名(实参列表)
.通过常对象只能调用它的常成员函数。
(3)常数据成员
.使用const说明的数据成员
.定义时必须初始化
在类初始化类表中进行初始化

2.3 类关系

2.3.1 友元

(1)友元是C++提供的一种破坏数据封装和数据隐藏的机制。
(2)通过将一个模块声明为另一个模块的友元,一个模块能够引用到另一个模块中本是被隐藏的信息,即私有成员。
(3)可以使用友元函数和友元类。
(4)为了确保数据的完整性,及数据封装与隐藏的原则,建议尽量不使用或少使用友元。

1、友元成员函数
友元函数是在类声明中由关键字friend修饰说明的非成员函数(即不是类的成员),在它的函数体中能够通过该类对象名访问 private 和 protected成员
作用:增加灵活性,使程序员可以在封装和快速性方面做合理选择。
注意:访问对象中的成员必须通过对象名
2、友元类
若一个类为另一个类的友元,则此类的所有成员都能访问对方类的私有成员。
声明语法:将友元类名在另一个类中使用friend修饰说明。
注意:访问对象中的成员必须通过对象名

3、前向引用声明:解决不同类间相互引用或引用的声明或定义问题
(1)使用条件:仅限于B类的引用或指针,因为使用前必须先定义,声明就只能引用其地址。
(2)实例
class B;//前向引用声明–》使用条件:仅限于B类的引用或指针,因为使用前必须先定义,声明就只能引用其地址
class A
{ public:
void f(B &b);
};
class B
{ public:
void g(A a); //但类A在前面已经定义,就可以直接
};

2.3.2 类的组合

1、概念:类中的成员数据是另一个类的对象。
作用:可以在已有抽象的基础上实现更复杂的抽象
2、类组合的构造函数
原则:不仅要负责对本类中的基本类型成员数据赋初值,也要对对象成员初始化。
声明形式(类外的格式):
本类名::本类名(对象成员所需的形参,本类成员形参) :对象1(参数),对象2(参数),…
{ 本类初始化 }
3、类组合的构造函数调用
构造函数调用顺序:
先调用内嵌对象的构造函数(按内嵌时的声明顺序,先声明者先构造)。然后调用本类的构造函数。(析构函数的调用顺序相反)
若调用默认构造函数(即无形参的),则内嵌对象的初始化也将调用相应的默认构造函数。

2.3.3 继承和派生

注意:
(1)派生类不能继承基类的构造函数和析构函数
(2)基类对象与派生类对象的类型兼容性
一个公有派生类的对象在使用上可以被当作基类的对象,反之则禁止:
A: 基类对象可以赋值为派生类对象
B: 派生类对象可以给基类对象的引用进行赋值
C: 派生类对象的地址可以赋值给指向基类对象的指针变量赋值,也就是指向基类对象的指针也可指向派生类对象
以上是针对public的继承,protect/private则不可行;反过来也不可以,即基类对象不可以给派生类对象赋值。
(3)基类的static成员继承
基类和派生类共享基类的静态成员变量的内存;

1、概念
继承:保持已有类的特性而构造新类的过程称为继承。
派生:在已有类的基础上新增自己的特性而产生新类的过程称为派生。
被继承的已有类称为基类(或父类);通过继承派生出的新类称为派生类。

2、继承与派生的目的
继承的目的:实现代码重用。
派生的目的:当新的问题出现,原有程序无法解决(或不能完全解决)时,需要对原有程序进行改造。

3、派生类的声明
class 派生类名:继承方式 基类名1,继承方式 基类名2,…
{
成员声明;
}
4、继承方式及访问属性
(1)不同继承方式的影响主要体现在:
.派生类成员对基类成员的访问权限
.通过派生类对象对基类成员的访问权限
(2)三种继承方式:
公有继承
继承时保持基类的各成员属性不变;派生类的成员只能访问基类的public和protected成员(直接调用即可);派生类对象只能生类的中的public成员;
私有继承
继承时保持基类的各成员属性均变为private;派生类的成员只能访问基类的public和protected成员(直接调用即可);派生类对象不能访问基类的中的任何成员;
保护继承
继承时保持基类的各成员属性均变为protect;派生类的成员只能访问基类的public和protected成员(直接调用即可);派生类对象不能访问基类的中的任何成员;
(3)继承方式选择(类间的关系):
is a: 包含关系,public—》继承关系
has a: 组合关系–》通过定义类的属性实现

5、派生类构造函数
(1)派生类构造函数
.基类的构造函数和析构函数不能被继承,派生类中需要声明自己的构造函数;
.声明构造函数时,需要对本类中新增成员进行初始化,也该对继承来的基类成员的初始化,自动调用基类构造函数完成。
.派生类的构造函数需要给基类的构造函数传递参数
(2)派生类构造函数声明格式:
派生类名::派生类名(基类所需的形参,本类成员所需的形参):基类名1(参数表),基类名1(参数表),…
{
本类成员初始化赋值语句;
};
(3)基类构造函数与派生类构造函数关系
A: 当基类中声明有默认形式的构造函数或未声明构造函数时,派生类构造函数可以不向基类构造函数传递参数。
B: 若基类中未声明构造函数,派生类中也可以不声明,全采用默认形式构造函数。
C: 当基类声明有带形参的构造函数时,派生类也应声明带形参的构造函数,并将参数传递给基类构造函数。

(4)构造函数的调用顺序
1. 调用基类构造函数,调用顺序按照它们被继承时声明的顺序(从左向右)。
2. 调用成员对象的构造函数,调用顺序按照它们在类中声明的顺序。
3. 派生类的构造函数体中的内容。

2.3.4 重载 覆盖 隐藏

(1)重载
A: 函数重载
定义:具有相似功能的同名函数,但它们参数不同;
要求:函数的参数个数不同或者参数的类型至少有一个不同
函数返回类型没有要求
B: 运算符重载
后面04天讲到。
(2)覆盖(重写)
覆盖(override)是指派生类中存在重新定义的函数,其函数名、参数列表、返回值值类型必须同父类中的相应的函数严格一致。覆盖就是派生类函数覆盖基类函数
特点:
1:函数名相同
2:参数相同(类型、个数)
3:基类对应函数必须是虚函数(virtual)
(3)隐藏
隐藏与类的作用域相关联。
Int x; //全局变量
Void func()
{
double x; //局部变量,全局变量被隐藏
cin>>x;
}

主要用于派生中,隐藏继承的基类成员;如果派生类中添加了新成员,其与基类同名,本地成员隐藏了继承来的成员;

2.3.5 多态

注意:
(1)静态成员、构造函数不能是虚函数,析构函数可以是虚函数。

多态性是指发出同样的消息被不同类型的对象接收时有可能导致完全不同的行为。
多态的实现:
函数重载
运算符重载
虚函数
1、多态形式
(1)静态多态
1)函数多态
函数多态变现为函数和运算符重载,函数重载和运算符重载可以通过使用同样的函数名和同样的运算符来完成不同的数据处理和操作。
2)宏多态
3)模板多态
后面第04天讲到
(2)动态多态
C++的动态多态性是实现面向对象技术的基础;通过虚函数实现;

2、虚函数概念
(1) 定义
. 虚函数是动态绑定的基础。
. 是非静态的成员函数。
. 在类的声明中,在函数原型之前写virtual。
. virtual 只用来说明类声明中的原型,不能用在函数实现时。
. 具有继承性,基类中声明了虚函数,派生类中无论是否说明,同原型函数都自动为虚函数。
(2)要求
虚函数继承后重写必须函数名、返回值类型、参数个数和类型都相同
(3)本质
不是重载声明而是覆盖。
(4)调用方式
通过基类指针或引用,执行时会根据指针指向的对象的类,决定调用哪个函数根据指针指向的对象的类,决定调用哪个函数。

3、静态绑定与动态绑定
只有类中的普通成员才能是虚函数。
绑定
程序自身彼此关联的过程,确定程序中的操作调用与执行该操作的代码间的关系。
静态绑定
绑定过程出现在编译阶段,用对象名或者类名来限定要调用的函数。
动态绑定
绑定过程工作在程序运行时执行,在程序运行时才确定将要调用的函数。

4、纯虚函数和抽象类
(1)纯虚函数
纯虚函数
(2)带有纯虚函数的类称为抽象类:
A: 声明格式:
class 类名
{
virtual 类型 函数名(参数表)=0; //纯虚函数

}

B:作用
抽象类为抽象和设计的目的而声明,将有关的数据和行为组织在一个继承层次结构中,保证派生类具有要求的行为。
对于暂时无法实现的函数,可以声明为纯虚函数,留给派生类去实现。
C: 注意
.抽象类只能作为基类来使用。
.不能声明抽象类的对象。
.构造函数不能是虚函数,析构函数可以是虚函数。

5、虚析构函数
当基类的派生类涉及到动态内存分配时,基类的析构函数应置为虚析构函数
(1)何时需要虚析构函数
A: 当你通过基类指针删除派生类对象时(派生类对象先赋值给基类对象)
删除一个派生类对象时,应该先调用派生类的析构函数,然后调用基类的析构函数。
B: 如果你打算允许其他人通过基类指针调用对象的析构函数(通过delete这样做是正常的),并且被析构的对象是有重要的析构函数的派生类的对象,就需要让基类的析构函数成为虚拟的。

2.3.6 运算符重载

注意:
(1)运算符重载的实质
运算符重载是对已有的运算符赋予多重含义
A: 必要性
C++中预定义的运算符其运算对象只能是基本数据类型,而不适用于用户自定义类型(如类)
B: 实现机制
. 将指定的运算表达式转化为对运算符函数的调用,运算对象转化为运算符函数的实参。
. 编译系统对重载运算符的选择,遵循函数重载的选择原则。

(2)规则和限制
A: 可以重载C++中除下列运算符外的所有运算符: . .* :: ?: sizeof
B: 只能重载C++语言中已有的运算符,不可臆造新的。
C: 不改变原运算符的优先级和结合性。
D:不能改变操作数个数。
E:经重载的运算符,其操作数中至少应该有一个是自定义类型。

1、重载为类成员函数
(1)声明形式
函数类型 operator 运算符(形参)
{

}
(2)参数
A: 重载为类成员函数时
参数个数=原操作数个数-1 (后置++、—除外)(减一是因为this指针为默认参数代表本类对象)
B: 重载为友元函数时
参数个数=原操作数个数,且至少应该有一个自定义类型的形参。

(3)双目运算符重载
如果要重载 B 为类成员函数,使之能够实现表达式 oprd1 B oprd2,其中 oprd1 为A 类对象,则 B 应被重载为 A 类的成员函数,形参类型应该是 oprd2 所属的类型。
经重载后,表达式 oprd1 B oprd2 相当于 oprd1.operator B(oprd2)

(4)单目运算符重载
1)前置单目运算符 U(–i,++i)
如果要重载 U 为类成员函数,使之能够实现表达式 U oprd,其中 oprd 为A类对象,则 U 应被重载为 A 类的成员函数,无形参。
经重载后,表达式 U oprd 相当于 oprd.operator U()

2)后置单目运算符 ++和-- (i++,i–)
如果要重载 ++或–为类成员函数,使之能够实现表达式 oprd++ 或 oprd-- ,其中 oprd 为A类对象,则 ++或-- 应被重载为 A 类的成员函数,且具有一个 int 类型形参。
经重载后,表达式 oprd++ 相当于 oprd.operator ++(0)

2、重载为非成员函数(通常为友元函数)
1:注意
(1) 如果需要重载一个运算符,使之能够用于操作某类对象的私有成员,可以将此运算符重载为该类的非成员(通常为友元)函数。
(2) 函数的形参代表依自左至右次序排列的各操作数。
(3) 后置单目运算符 ++和–(i++, i–)的重载函数,形参列表中要增加一个int,但不必写形参名。

2:重载后调用表达式
(1)双目运算符 B重载后,
表达式oprd1 B oprd2
等同于operator B(oprd1,oprd2 )
(2)前置单目运算符 B重载后,(++i,–i)
表达式 B oprd
等同于operator B(oprd )
(3)后置单目运算符 ++和–重载后,(i++,i–)
表达式 oprd B
等同于operator B(oprd,0 )

2.3.7 模板

所谓的函数模版,实际上就是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模版。目的是为了实现一个函数多用,将实现相同的或类似功能的函数用一个函数名来定义,即实现模板多态。

1、通用函数模板
声明和定义时都要加关键字template。
(1)通用函数定义
template
T/void 函数名(T 参数1,T 参数2,…) { … }

template<class T>
函数名<T>(T 参数1,T 参数2,...)		{ ... }

class和typename的作用相同,都是表示“类型名”,二者可以互换。
(2)调用格式

1:通用函数模板:
函数名<实际类型>(实参1,实参2,…);
2:一般用在函数模板的特例化:
函数名(实参1,实参2,…);
两种调用方式都可以,第一种方法只是更明了;
在建立模版函数时,只要将第一个函数用虚拟的类型名T代替具体的数据类型。在对程序进行编译时,遇到模版函数,编译系统会将函数名与模版相匹配,将实参的类型取代函数模版中的虚拟类型T。

2、类模板
(1)代码重用方式
. 继承
. 组合
. 模板
(2)类模板
“模板(template)”关键字会告诉编译器下面的类定义将操作一个或更多的非特定类型.当对象被定义时,这些类型被指定以使编译器能够替代它们.
A:模板类声明
template class Stack {void push (T a);…};
类中相关变量、参数和返回值类型用T代替
B: 函数实现
可以在类声明内或外部实现。在外部实现要有关键template 标识
template void Stack::push (T a){…}
C:使用
Stack s;

 例1数组类模板
 template<class T>
 class Array
{
 public:		
         T& operator[] (int index);
private:	
          enum { size = 100 };	
          T A[size];
};

若将函数作为非内联
template<class T>
T& Array<T>::operator[] (int index)
{
        return A[index];
}

int main()
{
       Array<int> ia;
      Array<float> fa;
      for(int i = 0; i < 20; i++)	
      {		
          ia[i] = i * i;		
          fa[i] = float(i) * 1.414;
     }	
     for(int j = 0; j < 20; j++)	
    {		
          cout<<j<<": "<<ia[j]<<", "<<fa[j] <<endl;	
    }
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值