1.18k likes | 1.34k Vues
第二章 C++ 概述. 2.1 C++ 的起源和特点. 2.1.1 C++ 的起源 2.1.2 C++ 的特点: ( 1 ) C++ 保持与 C 兼容,许多 c 程序不经修改就可以被 C++ 所用。 ( 2 )软件的可重用性和可维护性好。 ( 3 )支持面向对象机制,可方便的模拟现实问题的实体。 (4)c++ 编写的程序可读性更好,代码结构更合理。 (5) 程序运行效率更高。. 1. 2.2 C++ 源程序的构成 2.2.1 C++ 程序的一般格式 class student{
E N D
2.1 C++的起源和特点 2.1.1 C++的起源 2.1.2 C++的特点: (1) C++保持与C兼容,许多c程序不经修改就可以被C++所用。 (2)软件的可重用性和可维护性好。 (3)支持面向对象机制,可方便的模拟现实问题的实体。 (4)c++编写的程序可读性更好,代码结构更合理。 (5)程序运行效率更高。 1
2.2 C++源程序的构成 2.2.1 C++程序的一般格式 class student{ private: int number; char name[10]; int age; float score; public: void printname( ){cout<<name;} void printage( ){cout<<age;} void printheight( ){cout<<score;} }; main( ) { student s1; s1.printname( ); } 4
C++程序的退化格式说明C++构造的示例程序 #include <iostream.h> // 系统头文件 int add(int a, int b); //函数原型的说明 int main( ) //主函数 { int x,y,sum; //定义三个整型变量 cout<<"输入二个数:"<<'\n'; //界面:提示用户输入二个数 cin>>x; //从键盘输入变量x的值 cin>>y; //从键盘输入变量y的值 sum=add(x,y); //调用函数add,将函数返回值赋sum cout<<"二数和是:"<<sum<<'\n'; //输出 return 0; } int add(int a, int b) //定义函数add,函数(返回)值为整型 { int c; //定义临时变量c c=a+b; //求和 return c; } //将c的值返回,通过add带回调用处 2
2.3 C++在非面向对象方面的一些特性 2.3.1 注释行 /* */ C的注释 // <CR> C ++的注释 注意:/*…*/方式的注释不能嵌套, /*this is a multiline /*inside of */ here is the end*/错误 但//和/* */方式的注释可以互相嵌套。 /*this is a multiline//inside of which here is the end*/ //this is a multiline/*inside of which*/
2.3.2 新的I/O流 cin是标准输入流,在程序中代表标准输入设备,即键盘。 cin>>表达式>>表达式 int x,y; cin>>x>>y; cout是标准输出流,在程序中代表标准输出设备,即屏幕。 cout<<表达式<<表达式 cout<<“the result”<<x<<y; 使用cin或cout进行I/O操作时,在程序中必须嵌入头文件iostream.h //例2.2 #include <iostream.h> int main( ) { char name[20]; cout<<"Hello, your name:"; cin>>name; cout<<name<<endl; return 0; } //例2.3 #include <iostream.h> void main( ) { int x=25; cout<<hex<<x<<‘\n'<<dec<<x <<‘\n‘<<oct<<x<<endl; } 4
2.3.3 灵活的局部变量说明 int f( ) { int i; int j; i=10; j=25; } int f( ) { int i; i=10; int j; j=25; for(int k=5;k>=0;k--) ….. ……. } 以上的写法在C++程序中都是正确的。
2.3.4 结构、联合和枚举名可直接作为类型名 定义枚举类型名: enum bool {FALSE, TRUE}; 定义结构类型名: struct student { int sno; char *sneme; }; C语言中定义枚举类型变量: enum bool done; 定义结构类型变量: struct student s1, s2; C++语言中定义枚举类型变量: bool done; 定义结构类型变量: student s1, s2;
2.3.5 const修饰符 #define LIMIT 100 这里 LIMIT 是一个标志,代表 100 const 类型名 常量名=常数; 类型名 const 常量名=常数; const int LIMIT=100; // const LIMIT=100; int const LIMIT=100;// const LIMIT=100; 这里 LIMIT 是一个常量名,在内存空间放了100,因此有地址,可以用指针指向这空间,但不能修改它。
#include <iostream.h> main( ) { int a=1; #define T1 a+a #define T2 T1-T1 cout<<"T2 is "<<T2<<endl; //cout<<“T2 is “<<T1-T1<<endl; //cout<<“T2 is”<<a+a-a+a<<endl; return 0; } #include <iostream.h> main( ) { int a=1; const T1=a+a; const T2=T1-T1; cout<<"T2 is "<<T2<<endl; return 0; } 8
a const 可以和指针一起使用,他们的组合可归纳为三种。 (1) 可以用一个指向常量的指针变量指向常量: const char *name=“chen”; name[3]=‘a’; //错误 name=“zhang”;//正确 (2)指针常量指向变量 char *const name=“chen”; name[3]=‘a’; //正确 name=“zhang”; //错误 (3)常指针指向常量 const char *const name =“chen”; 常量 chen n name 常量 zhang name chen n a a 常量 zhang
说明: (1)常量也可以用变量初始化。 const 类型名 常量名=变量名; float a=3.4; const float b=a;a=4.5;cout<<b; 常量在其有效的范围内不能再赋值。而变量可以多次赋值。 (2)常量一旦初始化,在其作用域内都不能修改。 #include<iostream.h> void f( ) {const a=100; cout<<a; } void main( ) {float a; a=3.3; cout<<a; f( ); }
(3)常量可以作为函数参数。 int f ( const int k) { const int j=k*k; cout<<k; return j; } void main( ) {cout<<f(3);} void t(const int i) {++i; //error}
float circle(float r) { return 3.1416*r*r; } 2.3.6 内置函数 inline float circle(float r) { return 3.1416*r*r; } #include <iostream.h> int main( ) { for(int i=1;i<=3;i++) cout<<"r="<<i<<" area="<<circle(i)<<endl; return 0;} 在函数前加以 inline ,成为内置函数 #include <iostream.h> int main( ) 代码嵌入 { for(int i=1;i<=3;i++) cout<<"r="<<i<<" area="<<circle(i)<<endl; return 0; } 优点1:可以消除函数调用时的系统开销,以提高运行的速度。 3.1416*r*r 9
//例2.7 #include <iostream.h> #define doub(x) x*2 int main( ) { for(int i=1;i<=3;i++) cout<<doub(i)<<endl; cout<<doub(1+2)<<endl; return 0; } //例2.8 #include <iostream.h> inline int doub(int x) { return x*2; } int main( ) { for(int i=1;i<=3;i++) cout<<doub(i)<<endl; cout<<doub(1+2)<<endl; return 0; } 优点2:可以消除宏定义的不安全性 1 4 6 5 1 4 6 6 1+2*2 10
注意: ① 内联函数体内一般不能有循环语句和switch语句。 ② 内联函数的定义必须出现在第一次被调用之前. inline float circle(float r) { return 3.1416*r*r; } int main( ) { for(int i=1;i<=3;i++) cout<<"r="<<i<<" area="<<circle(i)<<endl; return 0; } ③只有很简单而使用频率很高的函数才被说明为内联函数。
2.3.7 函数原型 返回类型 函数名(参数表); //例2.9 #include <iostream.h> void write(char *s); //函数原型或叫函数申明 void main( ) { write("Hello, world!"); } void write(char *s)无分号 { cout<<s<<endl; } 。。。。。。。。。。。。 #include <iostream.h> void write(char *s) { cout<<s<<endl; } void main( ) { write("Hello, world!"); } 11
说明: (1)函数原型的参数表中可不包含参数的名字,而只包含参数的类型,但函数头中必须给出参数的名字。 如:int cal(int ,int); int main( ) { … } int cal(int a,int b) {return a*b;} (2)如果函数原型中没有任何参数,则C++假定参数表为空(void) int cal( );等价于 int cal(void);
(3)函数头中没有指出返回类型的函数(包括主函数),C++默认该函数的返回类型为int。(3)函数头中没有指出返回类型的函数(包括主函数),C++默认该函数的返回类型为int。 cal(int a,int b){…} 等价于int cal(int a,int b){…} main( ) {… return 0;} (4)如果一个函数没有返回值,则必须在函数名前注明返回类型为void。 void cal( ){cout<<“good”;} void main( ){ }
#include <iostream.h> void main() { int m,n,l,s; int maxj(int a, int b); int minb(int a, int b); cout<<"输入2个正整数:"; cin>>m>>n; if (m>0 && n>0) { l=maxj(m,n); s=minb(m,n); cout<<i<<endl; cout<<s<<endl; } else cout<<"输入了负数!\n"; return; } • int maxj(int a, int b) • { int r,t; • if(a<b) {t=a;a=b;b=t;} • r=a%b; • while(r!=0){ • a=b; b=r; • r=a%b; • } • return b; • }//最大公约数 • int minb(int a, int b) • { int r; • r=maxj(a,b); • if (r!=0) • return a*b/r; • else • return 0; • }//最小公倍数 33
2.3.8 带有缺省参数的函数 C++在说明函数原型时,可以为一个或多个参数指定缺省参数值,以后调用此函数时,若省略其中某一实参,C++自动以缺省值作为相应參数的值。 #include<iostream.h> void special(int x=5, float y=5.3); void main( ) {special( );//第一次 special(25);//第二次 special(100,79.8);//第三次 } void special(int x,float y) {cout<<x*y; } 12
#include<iostream.h> void special(int x=5, float y=5.3) {cout<<x*y; } void main( ) {special( ); special(25); special(100,79.8); }
说明: (1)所有取缺省值参数都必须出现在不取缺省值参数的右边。 int fun(int i,int j=5, int k); 是错误的 int fun(int i, int k,int j=5); 是正确的 int fun(int i, int k=4,int j=5); 是正确的 (2)调用时,若某个参数省略,则其后的参数皆应省略。 int fun(int i=1, int k=4,int j=5){cout<<i*k*j;} fun(2,3); fun(2); 不合法的调用; fun(, 3, 5); fim(, 3);
(3)在相同的作用域内,默认形参值的说明应保持唯一。但如果在不同的作用域内,允许说明不同的默认形参。(3)在相同的作用域内,默认形参值的说明应保持唯一。但如果在不同的作用域内,允许说明不同的默认形参。 add(int x=2,int y=5); //全局默认形参值 void main( ) { add(int x=1,int y=9); //局部默认形参值 add( ); //此处调用时,采用局部默认形参值,x取值1,y取值9 } void func( ) {add( ) ; //此处调用时,采用全局默认形参值,x取值2,y取值5 } add(int x,int y){cout<<x+y;}
2.3.9 函数重载 在C语言中,函数名必须是唯一的,例如:当要求编写求整数、浮点数和双精度度数的二次方的函数时,必须编写三个函数名不同的函数。 int isquare(int i) { return i*i; } float fsquare(float f) { return f*f; } double dsquare(double d) { return d*d; } C++中,只要函数的參数的类型不同,或者參数个数不同,或者二兼有之,几个函数可以用相同的函数名,称为函数重载.使函数方便使用,便于记忆,也使程序设计更加灵活。 13
//**例2.10 #include <iostream.h> int square(int i) { return i*i; } float square(float f) { return f*f; } double square(double d) { return d*d; } void main( ) { int i=12; float f=3.4; double d=5.67; cout<<i<<'*'<<i<<'='<<square(i)<<endl; cout<<f<<'*'<<f<<'='<<square(f)<<endl; cout<<d<<'*'<<d<<'='<<square(d)<<endl; }
//**例2.11 #include <iostream.h> int mul(int x, int y) { return x*y; } int mul(int x, int y, int z) { return x*y*z; } void main( ) { int a=3,b=4,c=5; cout<<a<<'*'<<b<<'='<<mul(a,b)<<endl; cout<<a<<'*'<<b<<'*'<<c<<'='<<mul(a,b,c)<<endl; } 14
说明: (1)重载函数应在参数个数或参数类型上有所不同,编译才能识别调用哪一个重载版本,即使返回类型不同也不行 如:int mul(int x, int y) ;和 double mul(int x, int y); 是不能重载的。 (2)一般而言,重载函数应对不同的参数情况执行相同的功能。 如:abs(int x); 和 abs(float x);都是求绝对值,一个是整数的绝对值,一个是实数的。 (3)函数的重载与带默认值的函数一起使用时,有可能引起二义性。 int mul(int x, int y) { return x*y; } int mul(int x, int y, int z=5) { return x*y*z; } 当调用mul(3,4)时,编译程序无法确定使用哪个函数。
2.3.10 作用域运算符:: 当全局变量和局部变量同名时,函数中不能使用全局变量。 //**例2.12 #include <iostream.h> int avar=10; //全局变量 void main( ) { int avar=25; //局部变量 cout<<"avar is "<<avar<<endl; {float avar=4.5; cout<<avar; } } 15
可用作用域运算符 :: //**例2.13 #include <iostream.h> int avar; void main( ) { int avar; avar=25; //局部变量avar ::avar=10; //全局变量avar cout<<"local avar = "<<avar<<endl; cout<<"global avar = "<<::avar<<endl; }
a 4字节 ch i f 2.3.11 无名联合 C语言的共用体: union data { int i; char ch; float f; } 共享同一内存地址,分配的地址空间的大小是按照最大的来进行分配的 union data a; 引用:a.i =3; a.ch =‘k’; a.f =7.9; 等 16
a 4字节 ch i f 无名共用体如下: union { int i; char ch; float f; } 以后在程序中任何地方都可以使用i,ch,f,而不需要声明,因为无名共用体已经分配好空间。 #include <iostream.h> void main(void) { union { int i; char ch; float f; }; i = 12; cout<<i; f=6.5; cout<<f; }
2.3.12 强制类型转换 隐式类型转换是系统自动实行的转换,主要出现在赋值语句和表达式语句中。 int a=3.45; float b=4; double c=a+b;
C语言的强制类型转换: (类型标识符)表达式 int i=10; float x=(float)i; int y=(int)(3+6.4) C++的强制类型转换: int i=10; float x=float (i); float y=(float) i; 两种方法C++都可以。 17
2.3.13 new和delete • 动态内存分配是相对于静态内存分配而言的。 • 静态内存分配是指在编译阶段就分配好存储单元空间,这些空间的大小在程序运行过程中是不可更改的,如变量、数组等; int i; • 动态内存分配则是指程序员在程序语句中通过调用内存分配函数或使用内存分配运算符取得存储空间,通过动态内存分配得到的空间的大小,编译器是不知道的,完全由动态运行中的程序的当时情况决定。
p C语言用函数malloc( )和free( )动态分配内存和释放动态分配的内存。C++使用运算符new和delete能更好、更简单地进行内存的分配和释放。 如C语言中用: int *p; p=(int *) malloc ( sizeof(int) ); *p=10; free(p); 18
而C++语言中 new运算符用于动态分配一块内存空间。new运算符的使用形式如下: 指针变量=new<数据类型>[长度] char*CBuffer; CBuffer=new char[256]; //分配一个可以容纳256个char型数据的空间 如果分配的空间长度为1个单位,则可以省略new运算符格式中的中括号和中括号中的整数。 float* pNum=new float; 与float* pNum=new float[1];等价。
//**2.14 #include <iostream.h> void main( ) { int *p; //定义一个整型指针变量p p=new int; *p=10; cout<<*p<<endl; delete p; //释放p指的空间,撤消指针p }
说明: (1)使用new运算符分配内存空间时,其空间长度可以是变量,也可以是数值表达式。例如: int nSize=5; int* nPInt=new int[nSize+5]; //分配一个可以容纳10个int型数据的空间 (2)使用new可以为数组动态分配内存空间。 int *pi=new int[10]; int *pi=new int[2][3][4]; 由new分配的内存空间是连续的,可以通过指针的变化访问所分配空间的每一个元素。 int*nPInt=new int[10]; nPInt[5]=100; 或 *(nPInt+5)=100; 19
(3)new可在为简单变量分配内存空间的同时,进行初始化。(3)new可在为简单变量分配内存空间的同时,进行初始化。 //**例2.15 #include <iostream.h> void main( ) { int *p; p=new int(99); //动态分配内存,并将99作为初始值赋给它 cout<<*p<<endl; delete p; }
(4)由new运算符分配的内存空间在使用完毕后,应该使用delete运算符释放。如果应用程序对有限的内存是只取不还,系统很快就会因为内存枯竭而崩溃。(4)由new运算符分配的内存空间在使用完毕后,应该使用delete运算符释放。如果应用程序对有限的内存是只取不还,系统很快就会因为内存枯竭而崩溃。 释放动态分配的数组存储区时,用如下格式: delete 指针 及 delete[ ]指针 int*pInt=new int; delete pInt; int* pManyInt=new int[10]; delete[ ]pManyInt; 用new运算符获得的内存空间,只许使用一次delete,不允许多次对同一块空间进行多次释放,否则将会产生严重错误。
(5)使用动态分配内存时,如果没有足够的内存满足分配要求,则分配失败,将返回空指针(NULL)。(5)使用动态分配内存时,如果没有足够的内存满足分配要求,则分配失败,将返回空指针(NULL)。 因此通常要对内存的动态分配是否成功进行检查。 分配内存失败,p=NULL //**例2.16 #include <iostream.h> void main( ) { int *p; p=new int; if(!p) //if (p==NULL) 若分配内存失败 { cout<<"allocation failure\n"; return ; } *p=20; cout<<*p<<endl; delete p; } p NULL 20
2.3.14 引用 引用简单的说就是别名,主要有变量的引用、函数參数的引用、函数返回值的引用。 1.引用变量 引用变量的声明形式为 <类型标识符> &引用名=变量名 int someInt; int& rInt=someInt 21
6 someint rint #include<iostream.h> void main( ) { int someInt; int& rInt=someInt; cout<<&someInt<<““<<&rInt<<endl; someInt=6; cout<<"someInt:"<<someInt<<endl; cout<<"rInt:"<<rInt<<endl; rInt=7; cout<<"someInt:"<<someInt<<endl; cout<<"rInt:"<<rInt<<endl; } 程序运行结果为 0xfff8 0xfff8 someInt:6 rInt:6 someInt:7 rInt:7
(1)在声明变量的引用时,必须立即对它进行初始化,不能以后再赋值。(1)在声明变量的引用时,必须立即对它进行初始化,不能以后再赋值。 int i; int &j; j=i; //错误 int &j=i; 引用的初始值可以为变量,也可以为另一个引用。 int i; int &j=i; int &k=j; //i有两个别名
(2) 定义完引用后,每次使用引用,可以直接通过引用名访问变量,而如果用指针,必须通过*间接访问变量。 #include <iostream.h> void main( ) { int i=15; //整型变量i int *iptr=&i; //iptr指向i int &rptr=i; //rptr引用i cout<<"i is "<<i<<endl; cout<<"*iptr is "<<*iptr<<endl; cout<<"rptr is "<<rptr<<endl; i=29; cout<<" After changing i to 29"<<endl; cout<<"i is "<<i<<endl; cout<<"*iptr is "<<*iptr<<endl; cout<<"rptr is "<<rptr<<endl; } i iptr rptr 22
(3)引用不可重新赋值,不可使其作为另一个变量的别名。(3)引用不可重新赋值,不可使其作为另一个变量的别名。 int i, k; int &j=i; j=&k; //错误 (4)并不是任何类型的数据都可以引用,下列都是错误的。 <1>不允许对空类型引用 void &a=10; <2>不能建立引用数组 int a[3]; int &r[3]=a; <3>不能建立指向引用的指针,不能建立引用的引用 int a; int &*p=a; int &&r=a; 23
(5)当使用取地址运算符时&,取的是被引用变量的地址。(5)当使用取地址运算符时&,取的是被引用变量的地址。 int num=50; int &ref=num; int *p=&ref; 则p中保存的是变量num的地址。
2. 引用作为函数的參数 <1>值传递方式 #include <iostream.h> void swap(int m, int n) { int temp; temp=m; m=n; n=temp; } main( ) { int a=5,b=10; cout<<"a="<<a<<" b="<<b<<endl; swap(a,b); cout<<"a="<<a<<" b="<<b<<endl; return 0; } a=5 b=10 a=5 b=10 a m b n tem a b 25
<2>地址传递调用的方式 #include <iostream.h> void swap(int *m, int *n) { int temp; temp=*m; *m=*n; *n=temp; } void main( ) { int a=5,b=10; cout<<"a="<<a<<" b="<<b<<endl; swap(&a,&b); cout<<"a="<<a<<" b="<<b<<endl; } a=5 b=10 a=10 b=5 a m &a temp b n &b 24