1
第二章 C++语言初步本章主要内容
1.C++语言的字符集、词汇
2.基本数据类型、运算符 与表达式
3.C++中的 常量 与变量
4.C++中的 I/O
5.基本控制结构
6.数组、结构体、共用体与枚举类型
7.指针,引用 与 动态内存分配
8.函数与 函数重载
11:17:57
2
§ 1 C++语言的字符集与词汇
1.字符集
(1)字母
A B C D … Z a b c d … z
(2)数字
0 1 2 3 4 5 6 7 8 9
(3)特殊符号空格 ! # $ % &,' " ( ) * +,-? /,;
< = > @ [ \ ] ^ _ { | } ~
11:17:57
3
2.词汇:五类
(1)关键字 (保留字 ):系统预定义且有专门含义的单词,不允许另作它用
.C++的关键字包含了 C语言的几乎所有关键字
.C++的关键字随 C++的发展而不断增加
(2)标识符:由程序员为程序中的各种成份定义的名字
.以字母或 _开头,由字母,_,数字组成
.不能是关键字
.C++中的标识符区分大小写
.见名知义的原则
.C++系统对标识符的长度有一定限制
(3)直接常量
(4)运算符
(5)分隔符:用来分隔不同的语法成份的单词空格 " ' ; { }# ( ) /* */ //
**C++的两种注释,
./*… */,// 注释到行尾
11:17:57
4
§ 2 基本数据类型、运算符与表达式一,数据类型
– 每一项数据都唯一的属于某种类型
– 每一数据类型都有一个明确定义的值的集合
– 每一类型的数据占用相同大小的存储空间
– 同一类型的数据可以进行相同类型的运算数据类型简单类型复合类型指针类型 *
用户定义类型 enum
基本类型整型 int
浮点型 float
字符型 char
空类型 void
布尔型 bool
数组 [ ]
结构体 struct
共用体 union
类 class
1.C++的数据类型基本类型是由系统定义,
各种语言都具备的类型复合类型与指针类型都属于用户定义类型
11:17:57
5
2.基本类型
– int型:整型 VC中占 4字节
– float型,浮点型 4字节
– char型,字符型 1字节
– void型,空类型
– bool型,布尔型 1字节 值为 true 或 false
**修饰说明符
long与 short,signed与 unsigned
– long和 short修改整型具有的最大值和最小值
– signed和 unsigned告诉编译器如何处理整型和字符型数据的最高位
– float型可用 double和 long double修饰,修改实型数据的表数范围和精度
11:17:57
6
类型名 字节数 取值范围
bool 1 true,false
char 1 -128 -- 127
[signed] char 1 -128 -- 127
unsigned char 1 0 -- 255
int 4 -231 -- 231-1
[signed] int 4 -231 -- 231-1
unsigned [int] 4 0 -- 232-1
[signed] short [int] 2 -32768 -- 32767
unsigned short [int] 2 0 -- 216-1
[signed] long [int] 4 -231 -- 231-1
unsigned long [int] 4 0 -- 232-1
float 4 -3.4E(+/-)38 -- 3.4E(+/-)38
double [float] 8 -1.7E(+/-)308 -- 1.7E(+/-)308
long double [float] 10 -3.4E(+/-)4932 -- 3.4E(+/-)4932
C++中的全部基本类型
11:17:57
7
二,运算符与表达式
1.运算符
– 算术运算符,+ - * / % ++ --
– 关系运算符,> >= == < <= !=
– 逻辑运算符,&& || !
– 位运算符,& | ^ ~ << >>
– 赋值运算符,= += -= *= /= %= <<= >>= &= |= ^=
– 条件运算符,?:
– 逗号运算符:,
– 指针运算符,& *
– 求字节运算符,sizeof
– 函数调用运算符,强制类型转换 运算符:()
– 分量运算符,,->
– 数组下标运算符,[]
– 作用范围分解运算符:,:
– 动态内存分配符,new,delete
– 插入提取运算符,<< >>
11:17:57
8
–运算符的优先级与结合性运算符 含义 要求操作对象数目 结合性
()
::
[ ]
,->
括号作用范围分解 (限定 )运算符数组下标运算符成员运算符左 ->右
++ --
&
*


+ -
( )
sizeof
new delete
自增 自减取地址指针引用逻辑非按位取反正 负号强制类型转换求字节动态分配 释放内存
1 右 ->左
* / % 乘法 除法 求余 2 左 ->右
+ - 加法 减法 2
11:17:57
9
–运算符的优先级与结合性(续表)
运算符 含义 要求操作对象数目 结合性
<< >> 左移 右移运算符
2 左 ->右
< <=
> >=
小于 小于等于大于 大于等于
== != 等于 不等于
&
^
|
按位与按位异或按位或
&& 逻辑与
|| 逻辑或
?,条件运算符 3 右 ->左
=
+= -= *= /= %=
<<= >>= &= ^= |=
赋值运算符扩展的赋值运算符 2 右 ->左
,逗号运算符 2 左 ->右
11:17:57
10
–C++中的类型转换
– 转换运算符:()
两种强制类型转换的方法,
(目标类型名 )表达式:
float x=2.5; int y; y=(int)x;
目标类型名(表达式):
float x=2.5; int y; y=int(x);
– C++中的显式转换
static_cast 可以不用强制类型转换但一定会发生转换的地方 (为了更清楚、理醒目而使用)
const_cast 对 const和 volatile进行转换
reinterpret_cast 转换为完全不同的意思,必须转换回原来的类型才能安全的使用,最为危险
dynamic_cast 把基类对象 (指针 )转换为派生类对象 (指针 )时使用
– 使用方式转换用关键字 <目标类型名 >(待转换的表达式 )
11:17:57
11
例,static_cast
void func(int){ }
void main()
{ int i=0x7fff; long l; float f;
l=i;f=i;
//更好的方法是使用 static_cast
l=static_cast<long>(i); f=static_cast<float>(i);
i=l;i=f;
//以上两条语句会出 Warning,可使用如下方式消除
i=static_cast<int>(l); i=static_cast<int>(f);
char c=static_cast<int>(i);
void *vp=&f;
float *fp=(float*)vp; //C的方法,不好
fp=static_cast<float*>(vp); //更好的方法
double d=0.0;
int x=d; //自动发生隐式类型转换
x=(int)d; //C的方法,不好
x=static_cast<int>(d); //更好的方法
func(d); //自动发生隐式类型转换
func(static_cast<int>(d)); //更好的方法
}
11:17:57
12
例,const_cast
void main()
{
const int i=0;
//int *j=&i; //错误,不允许这样赋值
int *k=(int*)&i; //被摒弃的方法
k=const_cast<int*>(&i);
//long *l=const_cast<long*>(&i);
//错误,不允许同时进行 const*->nonConst*
//和 nonConst*->long*两项转换
}
11:17:57
13
例,reinterpret_cast
#include <iostream>
using namespace std;
const int sz=100;
struct X{int a[sz];};
void print(X* x)
{
for (int i=0;i<sz;i++)
cout << x->a[i] << " ";
cout << endl << "-----------------" << endl;
}
void main()
{
X x;
print(&x);
int *xp=reinterpret_cast<int*>(&x);
for(int *i=xp;i<xp+sz;i++)
*i=10;
print(reinterpret_cast<X*>(xp));
print(&x);
}
11:17:57
14
2.表达式由运算符连接操作数构成的式子
– 算术表达式
– 关系表达式
– 逻辑表达式
– 赋值表达式
– 条件表达式
– 逗号表达式
11:17:57
15
§ 3 常量与变量一,C++中的常量
1.直接常量 (字面常量 ),10,10.5,‘ A?,? string”
.int型,float型,char型,字符串常量
.bool型,true,false
2.符号常量,C++中有两种符号常量
– #define定义的常量例,#define pi 3.1415926
– 关键字 const定义的常量例,const int sz=100;
– #define定义的常量,在预处理时只是字符串的替换,
对编译器而言,不带有任何类型信息,不便于查错;而
const定义的常量带有类型信息,故优于 #define定义的常量
– C++推荐使用 const定义的常量
11:17:57
16
– const常量定义语法,
const 类型名 常量名 =常量值;
或 类型名 const 常量名 =常量值;
例,const float pi=3.14;? float const pi=3.14;
const int min=50;? int const min=50;
const int max=2*min;? int const max=2*min;
const定义的常量,相当于定义了一个相应类型的变量,
但其值不能发生改变例,const int min=50;
min=40; //错误,不能改变 min的值所以,定义 const常量的同时必须初始化,且一旦初始化后,
就不会 (因不能故不会 )再发生变化 。 因此在作用范围内若某变量的值不会发生改变,则定义该变量时应用 const进行限定,以免无意中改变该变量的值
const常量是编译时的常量例,const int sz=100;
int arr[sz];
11:17:57
17
– const与指针
1.指向常量的指针,限定指针所指针对象、变量定义形式:
const 类型 * 指针变量 [=地址表达式 ];
或 类型 const * 指针变量 [=地址表达式 ];例,int var = 35;
const int max = 100;
int *p;
const int *p1_const;
const int *p2_const;
p1_const=&var;
p2_const=&max;
var=40;
*p1_const=100;
*p2_const=200;
max=200;
p=&max;
p1_const和
p2_const,
var不是常量
*p1_const

*p2_const
,max是常量
11:17:57
18
– 指向常量的指针
const int *p1_const
const int *p2_const const int max
int var
p1_const不是常量,p1_const指向的变量的值 不能 通过 *p1_const改变,但 p1_const指向的变量即 var本身不是常量,是 可以 改变的,故
*p1_const=100;
var=40;
p2_const不是常量,p2_const指向的变量的值 不能 通过 *p2_const改变,且 p2_const指向的变量即 max本身是常量,其值不能改变,故
*p2_const=200;
max=200;
11:17:57
19
2.指针常量,指针变量本身的值初始化后不能再改变定义方式:
类型 * const 指针变量 =地址表达式;
例,int var1=100,var2=200;
const int max=1000;
int * const cst_p1=&var1;
int * const cst_p2=&max;
*cst_p1=var2;
cst_p1=&var2;
只说明了指针变量如 cst_p1本身是常量,但并没有说不能改变 *cst_p1的值必须要赋初值
11:17:57
20
3.指向常量的指针常量指针变量本身和所指对象的值都为常量,不能改变定义方式:
const 类型 * const 指针变量 =地址表达式;
或 类型 const * cosnt 指针变量 =地址表达式;
例,int var1=128,var2=256;
const int max=1000;
const int *const doubleCst_p1=&var1;
const int *const doubleCst_p2=&max;
doubleCst_p1=&var2;
*doubleCst_p1=500;
var1=max-var2;
11:17:57
21
二,C++中的变量
–变量必须先定义 (声明 )再使用
–变量的定义存储类型 数据类型名 变量名列表;
– C++中 几乎 随处可以定义变量例:
for(int i=0;i<100;i+)…
if(i<100){
int x=0;
x+=100;
int y=10;
}
– 变量的分类:根据定义变量的位置,分为两类:
– 全局变量 (外部变量)
– 局部变量 (内部变量)
11:17:57
22
– 变量的存储类型
存储方式:动态存储方式和静态存储方式
静态存储方式:程序运行过程中给此类变量分配固定的存储空间
动态存储方式:程序运行过程中根据需要,给此类变量动态分配存储空间程序代码区静态存储区动态存储区
程序的存储组织
11:17:57
23
– 变量的存储类型
auto,register,static,extern和默认五种
auto:自动类型普通的局部变量都属于此类,分配在动态存储区中
register:寄存器类型
static:静态变量
静态局部变量:在静态区中,只初始化一次,多次调用函数时能保留前一次函数调用结束时的值,延长了该变量的 生存期
静态全局变量:在静态区中,限制了该变量的 作用域
extern:外部变量
定义全局变量
声明外部变量:扩展了全局变量的作用域
11:17:57
24
例,static
#include <iostream>
using namespace std;
int factor(int n){
static int result=1;
result*=n;
return result;
}
void main()
{
for(int i=1;i<=8;i++)
cout << factor(i) << " ";
cout << endl;
}
11:17:57
25
例,extern
//main.cpp
#include <iostream>
using namespace std;
extern int nTestVar;
void main()
{
nTestVar=100;
cout << nTestVar << endl;
}
//test.cpp
int nTestVar;
同一个工程的两个文件
11:17:57
26
变量的生存期与作用域
– 生存期:变量 a的生存期指从 a被说明且分配了内存开始,
直到该变量占用的内存被释放为止,是时间概念
– 作用域:变量 a的作用域指标识符 a可以代表该变量的范围,是空间概念
– 全局变量,生存期从程序开始运行到程序运行结束,而作用域则从定义该变量的地方开始,直到本文件末尾
– 普通局部变量,生存期从程序执行到包含该变量定义的复合语句时开始到该复合语句执行结束,而作用域则从该变量的定义处开始到包含该变量的程序块末尾
– 静态局部变量,生存期从为该变量分配了内存起直到程序结束,但作用域则与局部变量的作用域相同例,生存期与作用域
11:17:57
27
变量的初始化定义变量的同时给变量赋以初值
.两种方法
.例,int a=3;
.例,int a(3);
11:17:57
28
§ 4 C++中的输入 /输出一,输入 /输出方式
1.C语言的方式:
使用 scanf(),printf()等输入 /输出库函数
2.使用流对象的方式
– 把数据从一个对象向另一个对象的流动抽象成 流
– 流好比管道,从一端放入什么从另一端就能取出什么
– C++中的流对象一般与一个文件相关联,用流对象进行输出操作相当于把要输出的数据写入文件中,进行输入操作相当于从文件中读出数据存于变量或对象中
– 从流中获取数据的操作称为提取操作,流对象 >>变量向流中添加数据的操作称为插入操作,流对象 <<数据
<<,插入运算符 >>,提取运算符
– cin和 cout是 C++预定义的流对象
cout:用于进行输出操作,与显示器相关联
cin:用于进行输入操作,与键盘相关联
11:17:57
29
– << 与 >>
用法,cout<<表达式 1<<表达式 2<<… <<表达式 n;
cin>>变量 1>>变量 2>>… >>变量 n;
– <<可以串联多个 << 输出多个数据项,>>同样也可以串联多个 >>给多个变量输入数据
– >> 输入时多个数据项之间用空格,Tab或回车分隔
– << 和 >>具有智能,能够自动判断其后的数据项的类型,
并采用相应的类型进行输出或输入
– 使用流对象时,必须 #include相应的头文件 (如 iostream)
并使用语句? using namespace std;”打开名称空间 std
cin与 cout在 iostream中的 std名字空间中定义
11:17:57
30
二,简单的输入 /输出格式控制:使用操纵符 (manipulator)
– I/O流类库中提供了若干个操纵符,可以直接嵌入到输入 /
输出语句中实现 I/O格式控制操纵符 功能
endl 输出一个换行符,并刷新流
dec 用十进制输入或输出数值
oct 用八进制输入或输出数值
hex 用十六进制输入或输出数值
ends 输出一个空格符,并刷新流
setw(int n) 设置其后一个数据项占据的宽度
setfill(char c) 设置数据项宽度不足时的填充字符
setprecision(int n) 设置浮点数输出小数位数
– 常用的 I/O操纵符
11:17:57
31
说明:
– 不带参数的 I/O操纵符定义在 iostream中
– 使用带参数的 I/O操纵符,需包含 iomanip
– setw()只对其后紧跟着的一个真正的数据项起作用例,cout<<'A'<<ends<<'B'<<endl;
cout<<hex<<255<<ends<<32<<endl;
cout<<dec<<setw(5)<<setfill('0')<<32<<endl;
cout<<setw(5)<<setprecision(4)<<7.5612<<endl;
屏幕输出
11:17:57
32
§ 5 基本控制结构
– 顺序、选择 (分支 )和循环结构
– 对应的控制语句控制语句无条件控制语句条件控制语句分支语句循环语句
if语句
switch语句语句
for语句
while语句
do-while语句
break语句
continue语句
return语句
goto语句
11:17:57
33
§ 6 数组、结构体、共用体和枚举类型一,数组:需要处理相同类型的一批相关数据时使用
–数组分为一维数组与多维数组定义:
一维数组:类型 数组变量名 [数组长度 ];
二维数组:类型 数组变量名 [第一维长度 ][第二维长度 ];
例,int nArr[10];
int nArr2d[10][5];
说明:
– 数组长度必须是常量表达式:编译时求值
– C意义上的字符串 采用字符数组作为其存储形式,在最后添加一个 ASCII码为 0的字符作为 字符串 的结束标志
11:17:57
34
string类:字符串类
string类封装了字符串的基本特性和对字符串的典型操作,
其对象可用于保存几乎任意长的字符串,对处理输入文件中一行上字符数不定的情况特别有用
string类是 STL中的类,使用时需要包含头文件 string并打开名称空间 std
string类的操作:成员函数
求串长,size(),length()
例:
#include <iostream>
#include <string>
using namespace std;
void PrintAttribute(const string &str){
cout << "size," << str.size() << endl;
cout << "length,"<< str.length() << endl;
}
int main(){
string s1,s2;
PrintAttribute(s1);
s1="My string object"; PrintAttribute(s1);
s2="Another String Object"; PrintAttribute(s2);
return 0;
11:17:57
35
赋值,assign() =
s1.assign(s2);? s1 = s2;
连接,append += +
s1.append(s2);? s1+=s2;? s1=s1+s2;
下标,[]
s1[0]
#include <iostream>
#include <string>
using namespace std;
int main(){
string s1="cat ",s2,s3;
s2=s1; s3.assign("jump ");
cout << s2 << s3 << endl;
s1+=s3; cout << s1 << endl;
s1.append("and yell"); cout << s1 << endl;
s1[0]='h'; cout << s1 << endl;
return 0;
}
11:17:57
36
比较 string
s1.compare(s2);
比较字符串 s1与 s2,当 s1等于 s2时,返回 0;当 s1大于 s2时返回正值,否则返回负值
字符串的部分比较
s1.compare(beg1,len1,s2,beg2,len2);
beg1和 len1指示 s1参与比较的开始字符的下标与长度,
beg2和 len2指示 s2参与比较的开始字符的下标与长度
运算符,!= < > <= >=
11:17:57
37
例,string的比较,字符串的排序
#include <iostream>
#include <string>
using namespace std;
int main(){
string names[]={"Li Hua","He XiaoMing","Zhang Li",
"Sun Fei","Chen Bao"};
string s;
int i,j,k,nmb;
nmb=sizeof(names)/sizeof(names[0]);
for(i=0;i<nmb-1;i++){
k=i;
for (j=i+1;j<nmb;j++)if(names[k]>names[j]) k=j;
if(k!=i){
s=names[k];names[k]=names[i];names[i]=s;
}
}
for(i=0;i<nmb;i++)
cout << "name[" << i << "]=" << names[i] << endl;
return 0;
}
11:17:57
38
子串,substr()
s.substr(beg,len);
beg指定子串的开始下标,len指定读取的字符数
string s=“C++ program design”;
cout << s.substr(4,7) << endl;
查找,find()
s.find(substring);
s.find(substring,beginning);
替换,replace()
s.replace(beg,num,str);
11:17:57
39
例:把字母表中的逗号?,”全部替换为分号? ;”
#include <iostream>
#include <string>
using namespace std;
int main(){
string alphabet="A,B,C,D,E,F,G,H,I,J,K,L,M,
N,O,P,Q,R,S,T,U,V,W,X,Y,Z";
int x=alphabet.find(",");
while(x>=0) {
alphabet.replace(x,1,";");
x=alphabet.find(",",x+1);
}
cout << alphabet << endl;
return 0;
}
11:17:57
40
插入,insert()
s1.insert(n,s2);
例,string s1=“cat jump”;
string s2=“dog,;
s1.insert(4,s2);
s1.insert(4,“and,);
cout << s1 << endl;
11:17:57
41
转换成 C语言式的 char*型字符串,data() c_str() copy()
char * ptr=s.data(); //char * ptr=s.c_str();
s.copy(buffer,n,begin);
#include <iostream>
#include <string>
using namespace std;
int main(){
string s="String Object";
const char *ptr1;
ptr1=s.data();
int len=s.length();
char *ptr2=new char[len+1];
s.copy(ptr2,len,0);ptr2[len]='\0';
cout << s << endl;
cout << ptr1 << endl;
cout << ptr2 << endl;
delete []ptr2;
return 0;
}
11:17:57
42
从文件中输入一行字符,getline()
getline(in,str,ch);
其中,in为输入流对象
str为 string类对象
ch为结束输入的字符,默认为 ’ \n?
例,string s;
getline(cin,s,?\n?);
从键盘输入一行字符 (以回车结束 ),存入 s中
11:17:57
43
例:从文件中输入一行数据
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(){
string line;
ifstream input("input.txt",ios::in);
while(1){
getline(input,line);
if(input.eof()) break;
//一行数据已存入 line中,可对其处理,此处只将其输出
cout << line << endl;
}
input.close();
return 0;
}
11:17:57
44
二,结构体,struct
三,共用体,union
struct in_addr{
union{
struct
{unsigned chars_b1,s_b2,s_b3,s_b4;}S_un_b;
struct
{unsignedshorts_w1,s_w2;}S_un_w;
unsigned longS_addr;
}S_un;
};
四,枚举类型,enum ——与 C中几乎一样
**:自定义的数据类型可以象 C++的基本数据类型一样使用例,in_addr IP_Address;
11:17:57
45
§ 7 指针、动态内存分配与引用
.程序访问内存中数据的两种方式
.直接访问,间接访问
.指针就是地址,存放地址的变量就是指针变量
.指针变量的定义,
数据类型名 * 指针变量名;
或 数据类型名 * 指针变量名 =初始地址值;
200
X
200
X
&X
px
例,float *ptr;
float x=200.0;
float *px=&x;
float *pn=NULL;
float *pr;
指针变量的三种状态
.指向某一变量
.被赋予空值 NULL,未指向任何变量
.未赋值,处于? 悬空? (随机)状态一,指针
11:17:57
46
.与地址有关的运算符,& 和 *
.取地址运算 &:得到一个变量的地址
.一元运算符
.其后只能是一个变量名,不能是常量或表达式
.指针运算符 *:取指针指向的变量的值
.一元运算符
.其后为一指针例,*px=300.0;? x=300.0;
*与 &互为逆运算
11:17:57
47
.指针的运算
.算术运算指针可以和整数进行 +,-运算,指针变量本身也可以进行 ++,--运算指针的算术运算一般和数组的使用相联系
.关系运算,在指向相同类型数据的指针之间进行比较
.指针可以进行相等和不等的比较,指向同一变量 (地址 )
时相等,否则不相等
.任一指针可以和指针常量 NULL进行相等和不等的比较
.指针指向数组元素时,指针之间可以进行大小比较
11:17:57
48
.指针变量的使用
.指向一个变量,间接访问该变量
.指针变量与数组关联使用,
.指向数组元素例,int arr[10];
int *ptr;
ptr=&arr[0];//ptr=arr;数组名是数组首元素的地址
.指向数组定义方式:类型名 (*指针变量名 )[数组长度 ];
例,int a[2][4];
int (* pa)[4]; //定义 pa为指向一维整型数组的指
//针,该一维数组有 4个元素
pa=a; //? pa=&a[0];
pa++; // pa指向 a[1],则 pa==&a[1]
区别二者的算术运算使用时必须确保指向一个确定的有意义的空间
11:17:57
49
.指针数组,数组中的每个元素都相当于一个指针变量定义形式,类型名 *数组名 [数组长度 ];
例,int a,b,c;
int * parr[3];
parr[0]=&a;parr[1]=&b;parr[2]=&c;
.C字符串指针,可以把字符串当作一个整体进行操作
11:17:57
50
.指针变量与函数关联使用
.指针变量作函数参数,可改变实参指针所指变量的值
void swap(int *px,int *py){
int t;
t=*px;*px=*py;*py=t;
}
则 int a=3,b=5;
int *pa=&a,*pb=&b;
swap(pa,pb);//?swap(&a,&b);
调用后 a的值为 5,b的值为 3
void swap(int x,int y){
int t;
t=x;x=y;y=t;
}
则 int a=3,b=5;
swap(a,b); 调用后 a与
b的值 不会 发生变化,仍为 3
和 5
3
5
a
b
3
5
形参变量 x
形参变量 y
3
5
a
b
&a
&b
pa
pb
&a 形参指针 px
&b 形参指针 py
11:17:57
51
.函数返回指针例,
char *menu[]={“error”,”File”,”Edit”,”Search”,”Help”};
char *menuItem(int m)
{ return (m<1 || m>4)?menu[0]:menu[m]; }
**注意,返回的指针必须是有意义的,不能是一个指向函数内的局部变量或参数变量的指针或处于? 悬空? 状态的指针
.函数指针
.函数名代表函数的入口地址,也称做函数的指针
.可定义一指针变量,将函数的入口地址存入该指针变量中,通过该指针变量调用函数
.一般做函数的参数
11:17:57
52
二,动态分配内存
.两个运算符,new 与 delete
.new用来动态生成一个指定类型的无名变量,并返回该变量的地址,只能通过该地址 (指针 )访问动态生成的变量
.动态生成变量,int *pi;pi=new int;*pi=100;
.生成变量并初始化,int *pi=new int(5);
.动态生成一个数组,
int n; … //通过某种运算得到 n的值
int *pi=new int[n]; //此时数组长度可以是变量
.delete释放 new动态生成的变量
.释放动态生成的变量,delete pi;
.释放动态生成的数组,delete []pi;
例,动态内存分配注意,.new动态生成的变量不会自己撤消,所以该变量不用时,
必须及时用 delete将其释放
.new与 delete比 C中的函数 malloc和 free完善,所以 C++中不建议使用 malloc和 free动态申请与释放内存
11:17:57
53
三,引用
.引用即变量的别名,对引用的操作就是对目标变量的操作
.声明引用的一般形式,
类型名 & 引用名 = 目标变量名 ;
例,int nSomeVar;
int &ref = nSomeVar;
.引用不是变量,不占存储空间,故只能声明,没有定义
.由于引用不是变量,故不能声明引用的引用和指向引用的指针
.引用不做函数形参时,声明引用的同时必须对其初始化
.引用一旦初始化,就维系在其目标变量上,再也不分开,对该引用的任何操作,都是对引用维系的目标变量的操作,因此对引用的赋值就是对目标变量的赋值,而不是重新引用例,int nSomeVar = 0;
int nOther = 10;
int & ref = nSomeVar;
ref = nOther; //等价于 nSomeVar = nOther,而不
//是让 ref成为 nOther的引用
11:17:57
54
引用的作用
.给变量一个别名,通过别名实现对目标变量的操作
.做函数形参:此时 形参是实参的引用例,void swap(int &x,int &y){
int t;
t=x; x=y; y=t;
}
则 int a=3,b=5;
swap(a,b);调用后 a和 b的值分别是多少?
.引用做函数参数的特点
.引用做函数形参,实参一般使用变量而不使用常量或表达式
.可改变实参的值,若不需改变实参的值时可在形参引用前加上关键字 const,则由编译器保证不会无意中修改实参值
.可以使函数带回多个值
.可以减少函数调用过程中的开销
.比指针简洁方便,易使用,好理解
3
5
a
x
b
y
11:17:57
55
§ 8 函数与函数重载
函数的定义与声明
– 定义:
属性说明 返回值类型 函数名 (形参列表 ){函数体 }
其中的属性说明可以是以下几个关键字之一:
.inline,static,virtual,friend
.extern,C++中函数的缺省属性就是 extern
– 函数的声明,使用函数原型进行声明属性说明 返回值类型 函数名 (形参列表 );
**注意,
程序中对函数的调用出现在函数的定义之前时,调用之前必须进行声明
函数进行声明时形参只出现类型即可,可以不使用形参名
函数的声明与定义中返回值的类型必须一致,参数的类型,
数目必须相符一,函数
11:17:57
56
函数的使用,对函数进行调用形式,函数名 (实参列表 );
例,swap(a,b);
.函数调用过程中的参数传递
.值传递
.指针传递,仍为值传递,传递的是地址值
.引用传递,形参是实参的别名,完全代表了实参
11:17:57
57
函数的属性
.extern(外部 )函数,C++中函数缺省的属性就是外部的例,extern int func( ){ … }
则函数 func( )的作用范围不仅仅局限于定义它的文件中,
在其它文件中可以通过使用,extern int func()对其进行声明,从而在第二个文件中可以调用在第一个文件中定义的 func()函数
.因 C++中函数的缺省属性就是 extern的,所以定义和声明函数时通常省略关键字 extern
.static(静态 )函数例,static int func( ){ … }
static关键字限制函数 func( )的作用范围仅在定义它的文件中,其它文件中不可能通过声明的方式对该函数进行调用,即使进行了声明也无济于事
11:17:57
58
.inline(内联 )函数
.定义或声明函数时,在返回值类型前使用关键字 inline
进行修饰的函数例,inline int add(int x,int y){return x+y;}
.内联函数不被编译为单独一段可执行代码,而是把函数的可执行代码直接插入到对该函数的每一次调用处,所以没有函数调用过程中的开销,可以提高程序的效率
.与带参的宏相似,但因为函数的参数带有类型信息,所以比宏安全例,inline int max(int x,int y)
{return x>y?x:y;}
void main(){
int a,b;
cin >> a >> b;
cout << max(a,b) << endl;
}
11:17:57
59
例:内联函数
**注意
.若 inline不在函数名第一次出现时指定,则编译器把该函数做为普通函数对待
.内联函数的函数体应尽可能简洁,否则会增加程序的体积
.内联函数中不能包含复杂的流程控制语句,否则 inline关键字将不会起作用,该函数将成为普通函数
.inline函数不能递归,也不能包含静态变量
inline int isnumber(char);
void main(){
char c;
int n;
n=0;
while((c=getchar())!=?\n?)
{
if (isnumber(c)) n++;
}
cout <<,n=,<< n << endl;
}
int isnumber(char ch)
{
return (ch>=?0?
&& ch<=?9?)?true:false;
}
11:17:57
60
.带缺省实参值的函数声明或定义函数的同时,可对函数的某些参数进行初始化,该初始化的值称做缺省实参值。调用该函数时,可传递少于形参数目的参数,对缺少实参值的参数,函数使用缺省的实参值
.声明或定义形式,
类型名 函数名 (参数类型 1 参数名 1[=缺省值 1],…,参数类型 2 参数名 n[=缺省值 n])
{ … }
例,void showchar(char c,int times=20){
for(int i=1;i<=times;i++)
cout << c;
cout << endl;
}
调用时可使用如下形式,
showchar(?A?,10); //显示 10个字符 ‘ A?
showchar(?A?); //显示 20个字符 ‘ A?
**注意
.形参列表中一旦出现带有缺省实参值的形参,则其后的形参必须都带有缺省实参值
.调用时实参只能从右到左缺省
.形参的缺省实参值一般出现在函数的声明中
11:17:57
61
.返回引用的函数函数的返回值是一个变量的引用例,int & max(int &a,int &b){return a>b?a:b;}
则 int x=10,y=100;
max(x,y);
调用返回的是变量 y的引用,在变量 y的作用域内完全代表了变量 y
.函数调用 max(x,y)可以做左值
max(x,y)--;
max(x,y)=1000;
都是对变量 y进行操作
.函数返回的引用所引用的变量在该函数调用结束后应仍然存在并且有意义,不能返回函数内部的局部变量的引用
11:17:57
62
.递归函数,函数直接或间接调用函数本身例,
.n!
.Hanoi问题
11:17:57
63
二,函数重载
.C++中使用同一个名字对一组对不同数据类型进行相同或相似操作的函数命名的机制
.重载的函数可以是普通的函数也可以是类的成员函数例,int abs(int x)
{return x>0?x:-x;}
long abs(long x)
{return x>0?x:-x;}
float abs(float x)
{return x>0?x:-x;}
例,int max(int x,int y)
{return x>y?x:y;}
long max(long x,long y)
{return x>y?x:y;}
float max(float x,float y)
{return x>y?x:y;}
11:17:57
64
.函数重载的必要性
.C++实现函数重载的机制,
可能的方案:对函数名进行修饰例,三个 abs()函数
C++处理时,在内部给函数名加上了前缀或后缀,以区分重载的一组函数,进行修饰后三个函数的名字可能是:
abs_int() abs_long() abs_float()
.多个函数共用同一个名字,而这些函数进行的操作与完成的功能大体上相同,只是所操作的数据的类型不同,对这些函数以一个统一的名字调用,符合人的思维习惯,便于阅读理解程序
11:17:57
65
.函数修饰指示符
.extern,C”,函数按 C中方式处理,不允许重载
.extern,C++”,缺省方式 允许函数重载例:
extern,C”{
void f(){ }
void f(int){ }
}
extern,C”{
void f(){ }
void g(){ }
}
extern,C++”{
void f(){ }
void f(int){ }
}
11:17:57
66
**注意,
.重载的多个函数应在参数数目或参数的类型上有所区别
.函数的返回值不能用来区别重载的函数
.引用参数不能用来区别重载的函数
.函数带有缺省实参时可能造成二义性例,int sum(int a,int b,int c=0);
int sum(int a,int b);
应避免这种问题
.重载的函数应该是功能相同或相似的一组函数,不要把完全没有关系的函数进行重载
11:17:57