c++基础学习笔记

第一部分 c++基础知识
1
a) VS进行编译时应该先对文件进行“生成”或者“重新生成”操作,方法是右键点击文件,弹出菜单中有“生成”选项。
b) 用Dev编译器进行多文件连接编译时,将头文件(.h)放入Dev安装目录下MinGW64\include文件夹中进行编译。
2 数据的存储、表示形式和基本运算
2.1 C++的数据类型
表2.1 数值型、字符型数据的字节数和数值范围
类型 类型标示符 字节数 数值范围
整形 [signed]int 4 -2147483648~+2147483648
无符号整形 unsigned[int] 4 0~4294967295
短整型 short[int] 2 -32768~+32767
无符号短整型 unsigned short[int] 2 0~65535
长整形 long[int] 4 -2147483648~+2147483648
无符号长整形 unsigned long[int] 4 0~4294967295
字符型 [signed]char 1 -128~+127
无符号字符型 unsigned char 1 0~255
单精度型 float 4  
双精度型 double 8  
长双精度型 long double 8  
字符串类型 string    
逻辑型 bool   true、false
2.2 常量
2.2.1 数值常量
2.2.2 字符常量
2.2.2.1 普通字符常量
用单引号括起来的一个字符,如 ’a’、’A’、’#’,在内存中占一个字节,只包括一个字符,’ab’不合法。
2.2.2.2 转义字符常量
表2.2P22
2.2.2.3 字符串常量
用双引号括起来的字符,如”abc”。字符串常量中也可以包含转义字符,如”abc\n”是4个字符,占用5个字节,因为编译系统会在字符串最后自动加一个’\0’作为字符串结束标志,占用1个字节。
2.2.3 符号常量
为了编程和阅读的方便,可以用一个符号名代表一个常量,称为符号常量。在c++中一般用长变量,而较少使用符号常量。

define PRICE 30 //用预处理指令#define指定PRICE在程序中代表常量30,注意不是语句,后面没有 ;

2.2.3.1.1.1 举例代码

include

using namespace std;
int main()
{
char c1=’a’,c2=’b’,c3=’\101’,c4=’\102’,c5=’\105’;
cout <

include

using namespace std;
int main()
{
cout<<0+’a’<

include

using namespace std;
int main()
{
char c1,c2,c3,c4,c5;
c1=’C’,c2=’h’,c3=’i’,c4=’n’,c5=’a’;
c1+=4,c2+=4,c3+=4,c4+=4,c5+=4;
cout<

include

using namespace std;
int main()
{
int i,j,m,n;
i=8,j=10;
m=++i+j++;
cout<

include

using namespace std;
int main()
{
char c[5]={‘z’,’a’,’b’,’Z’,’A’} ;
int i;
for(i=0;i<5;i++)

    if ((c[i]>='A'&& c[i]<='Z') || (c[i]>='a'&& c[i]<='z')) 
    {
        c[i]+=4;
        if ((c[i]>'Z'&&c[i]<='Z'+4 )|| (c[i]>'z'&&c[i]<='z'+4) ) c[i]-=26;      
    }   

for (i=0;i<5;i++) cout<< c[i]   ;
return 0;   

}
3 程序设计初步
3.1 基于过程的程序设计和算法
3.1.1 算法的概念
算法:对操作的描述,为解决一个问题而采取的方法和步骤
数据结构:对数据的描述
程序=算法+数据结构
3.1.2 算法的表示
有 自然语言、流程图、伪代码、计算机语言(计算机程序) 四种
3.2 C++的程序结构和C++语句
1) 预处理指令,如 #include 指令和 #define 指令;
2) 全局声明,在函数外对数据类型、函数以及变量的声明和定义;
3) 函数,包括函数首部和函数体;
3.3 C++的输入与输出
3.3.1 cin 和 cout进行输入输出
3.3.1.1 输入流与输出流的基本操作
cin语句的一般格式:
cin>>变量1>>变量2>>变量3……
cout语句的一般格式
cout<<表达式1<<表达式2<<达式3……
3.3.1.2 在标准输入流与输出流中使用控制符
如果使用了控制符,在程序单位的开头要加 #include 头文件。
3.3.1.2.1.1 输入输出流使用控制符

include

include //声明流操作符

using namespace std;
int main()
{
char c;
c=003; //003为心形的ASCII码
cout<

include

include

include

using namespace std;
int main()
{
double r,h;
cout<< “请输入圆柱体半径”;
cin>>r;

double l, s;
const double pi=3.141592653;
l=2*r*pi ;
s=pi*(pow(r,2));
cout<<"圆的面积="<<s<<endl; 
cout<<"圆的面积="<<setiosflags(ios::fixed)<<setprecision(2)<<l<<endl; 

}

4 利用函数实现指定的功能
4.1 函数分类
4.1.1 系统函数(库函数)
4.1.2 用户自定义函数(无参函数、有参函数)
4.2 定义函数的一般形式
4.2.1 定义无参函数的一般形式
类型名 函数名(void)
{声明部分
执行语句}
4.2.2 定义有参函数的一般形式
类型名 函数名(形式参数列表)
{声明部分
执行语句
}
4.3 函数参数和函数的值
4.3.1 形式参数和实际参数
实参变量对形参变量的数据传递是“值传递”,即单项传递,只由实参传递给形参,而不能由形参传回给实参,因此在执行函数调用时,如果形参的值发生改变,并不会改变主调函数中实参的值。
4.3.2 函数的返回值
函数的返回值是通过函数中的 return 语句获得的。
4.3.2.1.1.1 调用实参函数求最值

include

using namespace std;

int max(int x,int y) //定义有参函数max
{
int z;
z=x>y?x:y;
return(z); //强z的值返回到主调函数中 ,即赋给下面的变量c
}

int main()
{
int a,b,c;
cout<<”please enter two integer numbers:”;
cin>>a>>b;
c=max(a,b);
cout<<”max=”<

include

using namespace std;

int a,b;

int main()
{
void change(int i,int j); //声明函数

cin>>a>>b;
cout<<a<<' '<<b<<endl;
void change(int i,int j);
cout<<a<<' '<<b<<endl;

}
void change(int i,int j)
{
int k;
k=i,i=j,j=k;
return 0;
}
指针变量作函数
4.4 函数的调用
4.4.1 函数调用的一般形式
函数名(实参列表)
4.4.2 函数调用的方式
4.4.2.1 函数语句
把函数调用作为一个语句,并不要求带回一个值,只要求函数完成一定的操作。
4.4.2.2 函数表达式
函数出现在表达式中,这时要求函数带回一个确定的值参加表达式运算。
4.4.2.3 函数参数
一个函数调用作为另一个函数的实参。
4.4.3 对被调用函数的声明和函数原型
如果使用库函数,应该在本文件开头用 #include 指令将有关头文件“包含”到本文件夹中。
如果被调用函数在主调函数之后,则必须在调用此函数之前对被调用函数进行声明(如果未进行声明,系统在编译时无法知道被调函数的含义,因而无法编译,按出错处理)。所谓函数声明,就是在函数尚未定义的情况下,事先将该函数的有关信息通知编译系统,以便编译系统能够正常进行。
对函数的声明称为函数原型
4.5 函数的嵌套调用

4.6 函数的递归调用

4.7 内置函数
4.8 函数重载
4.9 函数模板
建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。凡是函数体相同的函数都可以用这个模板来代替,只需定义一次即可,在调用函数时,系统会根据实参的类型来取代模板中的虚拟类型,从而实现不同函数的功能。
定义模板函数一般形式:
template
通用函数定义
4.10 有默认参数的函数
4.11 变量的作用域
4.11.1 局部变量
在一个函数内部定义的变量是内部变量,它只在本函数范围内有效,同样,在复合语句中定义的变量只在本复合语句范围内有效,这些内部变量称为局部变量。
4.11.2 全局变量
在函数内定义的变量是局部变量,而在函数之外定义的变量是外部变量,称为全局变量。全局变量的有效范围从定义变量的位置开始到本源文件结束。
4.12 变量的存储类别
存储方法分为静态存储和动态存储两大类,具体分为以下四种。
4.12.1 自动变量(auto)
4.12.2 静态局部变量(static)
4.12.3 寄存器变量(register)
4.12.4 外部变量(extern)
4.12.5 静态外部变量(static)
4.13 变量的声明和定义
建立存储空间的称为定义,不建立存储空间的称为声明。
定义变量:
int a;
声明变量:
extern int a;
所谓声明,其作用是向编译系统发出一个信息,声明该变量是一个在后面定义的外部变量,仅仅是为了提前引用该变量而做的声明。
4.14 内部函数和外部函数
4.14.1 内部函数
只能被本文件中其他函数所调用。在定义内部函数时,在函数类型的前面加关键词 static
4.14.2 外部函数
能被其他文件调用的函数称为外部函数,在定义外部函数时,在函数类型的前面加关键词 extern(如果省略关键字则默认为外部函数)
4.15 头文件和标准库
5 利用数组处理批量数据
5.1 定义和引用一维数组
5.1.1 定义一维数组
类型名 数组名 [常量表达式]
5.1.2 一维数组的初始化
5.1.2.1 在定义数组时对全部数组元素赋初值
int a[10]={0,1,2,3,4,5,6,7,8,9};
5.1.2.2 可以只给一部分元素赋初值
int a[10]={0,1,2,3 };
5.1.2.3 在对全部数组元素赋初值时,可以不指定数组长度
上述数组可以写为
int a[ ]={0,1,2,3 };
5.1.2.4 不能对整个数组一次赋值
如: a={0,1,2,3}是错误的
5.1.2.5 不能对整个数组整体赋值
如 a=b;(这里设定a、b均为正数数组)
5.1.3 引用一维数组
只能逐个引用数组元素的值而不能一次引用整个数组中的全部元素的值。
5.2 定义和引用二维数组
5.2.1 定义二维数组
类型名 数组名 [常量表达式] [常量表达式]
5.2.2 二维数组的初始化
5.2.2.1 按行给二维数组初始化
Int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
5.2.2.2 可以将所有数据写在一个花括号内,按数组排列的顺序对全部元素赋初值
5.2.2.3 可以对部分元素赋初值
这样其余元素值自动置为0
5.2.2.4 如果对全部元素都赋初值,则定义数组时对第1维的长度可以不指定,但第2维的长度不能省略
5.3 用数组作函数参数
数组元素可以作为函数实参,是将数组元素的值传递给新参变量
数组名也可以做实参和形参,传递的是数组的起始地址。用数组名作函数实参时,如果改变了形参数组元素的值,将同时改变数组元素的值,可以利用这一特点改变实参数组元素的值(因为函数传递的是地址,按“按值传递”的思想,这个值就是地址,它是不能通过改变返回实参的,但是它里面储存的数据是可以改变的,即实参的地址不变,但是里面的数据发生改变)。
5.3.1.1.1.1 十个整数由小到大排序

include

using namespace std;
int main()
{
void select_sort(int array[],int n); //声明函数 选择排序
int a[10],i;
cout<<”enter the originl array:”<

include

using namespace std;
int main()
{
void change(int *pointer1,int *pointer2); //声明函数
int a,b,*p1,*p2;
cin>>a>>b;
cout<

include

using namespace std;
int main()
{
void change(int *pointer1,int *pointer2); //声明函数
int a,b,*p1,*p2;
cin>>a>>b;
cout<

include

using namespace std;
int main()
{
void select_sort(int *p,int n); //声明函数 选择排序
int a[10],i;
cout<<”enter the originl array:”<

include

using namespace std;
int main()
{void swap(int x,int y);
int i=3,j=5;
swap(i,j);
cout <

include

using namespace std;
int main()
{void swap(int *x,int *y);
int i=3,j=5;
swap(&i,&j);
cout <

include

using namespace std;
int main()
{void swap(int &x,int &y);
int i=3,j=5;
swap(i,j);
cout <

include

using namespace std;
class Time
{public:
int hour;
int minute;
int sec;
};

int main()
{
Time t1;
cin>>t1.hour;
cin>>t1.minute;
cin>>t1.sec;

cout<

include

using namespace std;
class Time
{public: //公有数据和成员函数
void set_time(); //公用成员函数
void show_time();
private: //私有数据和成员函数
int hour;
int minute;
int sec;
};

void Time::set_time() //在类外定义set_time函数
{
cin>>hour;
cin>>minute;
cin>>sec;
}

void Time::show_time()
{
cout<

include

using namespace std;
class Time
{public: //私有数据和成员函数
int hour;
int minute;
int sec;
};

int main()
{
void set_time(Time&,int hour=0,int minute=0,int sec=0); //声明公用成员函数
void show_time(Time&);

Time t1;
set_time(t1,22,32,34);
show_time(t1);
Time t2;
set_time(t2);
show_time(t2);
system(“PAUSE”); //防止运行DOS程序时,它会自动关闭
return 0;
}

void set_time(Time&t,int hour=0,int minute=0,int sec=0) //在类外定义set_time函数
{t.hour=hour;
t.minute=minute;
t.sec=sec;
}

void show_time(Time&t)
{
cout<

include

using namespace std;
class Time
{public: //公有数据和成员函数
void set_time(Time&,int hour=0,int minute=0,int sec=0); //公用成员函数
void show_time(Time&);
private: //私有数据和成员函数
int hour;
int minute;
int sec;
};

int main()
{
Time t1;
t1.set_time(t1,11,11,11);
t1.show_time(t1);
Time t2;
t2.set_time(t2);
t2.show_time(t2);
system(“PAUSE”); //防止运行DOS程序时,它会自动关闭
return 0;
}

void Time::set_time(Time&t,int hour,int minute,int sec) //在类外定义set_time函数
{
t.hour=hour;
t.minute=minute;
t.sec=sec;
}

void Time::show_time(Time&t)
{
cout<

include

using namespace std;
class Array_max
{public:
void set_value(); //公用成员函数
void max_value();
void show_value();
private:
int array[10]; //数据成员为私有的
int max;
};
void Array_max::set_value() //在类外定义函数
{
int i;
for(i=0;i<10;i++)
cin>>array[i] ;
}

void Array_max::max_value()
{
int i;
max=array[0];
for(i=1;i<10;i++)
if(array[i]>max) max=array[i];
}
void Array_max::show_value()
{cout<<”max=”<

include

using namespace std;
class Time
{public: //公有数据和成员函数
Time() //无参数的构造函数
{hour=0;
minute=0;
sec=0;
}
void set_time(); //公用成员函数
void show_time();
private: //私有数据和成员函数
int hour;
int minute;
int sec;
};

void Time::set_time() //在类外定义set_time函数
{
cin>>hour;
cin>>minute;
cin>>sec;
}

void Time::show_time()
{
cout<

include

using namespace std;
class box
{public:
box(int,int,int); //声明带参数的构造函数
int volume(); //声明成员函数
private:
int length,width,height;
};
box::box(int l,int w,int h) //在类外定义构造函数
{length=l;
width=w;
height=h;
}
int box::volume() //在类外定义成员函数
{return(length*width*height);
}

int main()
{box box1(2,3,4); //建立对象,并给出参数,通过自动调用构造函数给对象赋初值
cout<<”长方体 box1 的体积为”<

include

using namespace std;
class box
{public:
box();
box(int,int,int); //两个函数名相同
int volume(); //声明成员函数
private:
int length,width,height;
};
box::box()
{length=10;
width=10;
height=10;
}
box::box(int l,int w,int h):length(l), width(w),height(h){}
int box::volume() //在类外定义成员函数
{return(length*width*height);
}

int main()
{box box1(2,3,4); //建立对象,并给出参数,通过自动调用构造函数给对象赋初值
cout<<”长方体 box1 的体积为”<

include

using namespace std;
class box
{public:
box(int l=10,int w=10,int h=10);
int volume(); //声明成员函数
private:
int length,width,height;
};

box::box(int l,int w,int h):length(l), width(w),height(h){}
int box::volume() //在类外定义成员函数
{return(length*width*height);
}

int main()
{box box1(2,3,4); //给定三个参数
cout<<”长方体 box1 的体积为”<

include

include

using namespace std;
class student
{public:
student(int n,string nam,char s):num(n),name(nam),sex(s)
{cout<<”学号”<

include

include

using namespace std;
class student
{public:
student(int n,string nam,char s):num(n),name(nam),sex(s){} //类内定义构造函数
void show_st(); //类内声明成员数

private:
int num;string name;char sex;
};
void student::show_st() //类外定义成员数
{
cout<

include

include

using namespace std;
class student
{public:
student(int n,string nam,char s):num(n),name(nam),sex(s){} //类内定义构造函数
void show_st(); //类内声明成员数
private:
int num;string name;char sex;
};
void student::show_st() //类外定义成员数
{
cout<

include

include

using namespace std;
class student
{public:
student(int n,string nam,char s):num(n),name(nam),sex(s){} //类内定义构造函数
public:
int num;string name;char sex;
};
int main()
{student st1(1001,”wangya”,’m’); //建立对象st1并初始化
int *pt; //定义student类对象的指针变量pt
pt=&st1.num; //使得指针pt指向对象st1 ,此时是在类外引用数据成员,所以其public型
cout<<*pt;
}
9.5.2.2 指向对象成员函数的指针
定义指向对象成员函数的指针变量的方法和定义指向普通函数的指针变量方法有所不同,应该在指针名称前加上类名和域运算符。一般形式:
数据类型名(类名:: *指针变量名)(参数列表); //定义指向成员函数的指针变量
指针变量名=&类名::成员函数名; //使指针变量指向一个公用成员函数

include

using namespace std;
class student
{public:
student(int n,string nam,char s):num(n),name(nam),sex(s){} //类内定义构造函数
void show_st(); //类内声明成员数

private:
int num;string name;char sex;
};

void student::show_st() //类外定义成员数
{
cout<

include

using namespace std;
class Time
{public: //公有数据和成员函数
Time (int h,int m,int s);
int hour;
int minute;
int sec;
};

Time::Time(int h,int m,int s):hour(h),minute(m),sec(s){} //在类外定义set_time函数
void fun(Time &t) //也可以是是类中的成员函数
{t.hour=18;}
int main()
{
Time t1(10,13,56);
fun(t1);
cout<

include

using namespace std;
class Time
{public: //公有数据和成员函数
Time (int h,int m,int s);
int hour;
int minute;
int sec;
};
Time::Time(int h=0,int m=0,int s=0):hour(h),minute(m),sec(s){} //在类外定义set_time函数
int main()
{
Time t1(10,13,56);
Time t2;
t2=t1;
cout<

include

using namespace std;
class Time
{public: //公有数据和成员函数
Time (int m,int s);
static int hour;
int minute;
int sec;
};
Time::Time(int m,int s):minute(m),sec(s){} //在类外定义set_time函数
int Time::hour=10;
int main()
{
Time t1(13,56);
Time t2(12,44);
cout<

include

using namespace std;
class Date; //对 Date类的提前引用声明
class Time
{public: //公有数据和成员函数
Time(int h=0,int m=0,int s=0):hour(h),minute(m),sec(s){}
void display(Date &); //声明成员函数 ,形参是Date类对象的引用
private: //私有数据和成员函数
int hour;
int minute;
int sec;
};
class Date
{public:
Date(int m=0,int d=0,int y=0):month(m),day(d),year(y){}
friend void Time::display(Date &d); //声明Time中的display函数为本类中的友元函数
public:
int month;
int day;
int year;
};
void Time::display(Date &d) //行参是Date类对象的引用
{cout<

include

using namespace std;
class Date; //对 Date类的提前引用声明
class Time
{public: //公有数据和成员函数
Time(int h=0,int m=0,int s=0):hour(h),minute(m),sec(s){}
friend Date; //声明 Date 类为本类的友元类
void display(Date &); //声明成员函数 ,形参是Date类对象的引用
private: //私有数据和成员函数
int hour;
int minute;
int sec;
};
class Date
{public:
Date(int m=0,int d=0,int y=0):month(m),day(d),year(y){}
public:
int month;
int day;
int year;
};
void Time::display(Date &d) //行参是Date类对象的引用
{cout<

include

using namespace std;
class complex
{public:
complex(){real=0;imag=0;}
complex(double r,double i):real(r),imag(i){}
void display();

complex operator+ (complex c2)  //对 + 的重载 ,+是有两个参数的,但由于是调用本类函数,故故另一个一个参数就是调用这个函数的对象
{complex c;
c.real=real+c2.real;
c.imag=imag+c2.imag;
return c;
}

private:
double real;
double imag;
};
void complex::display()
{cout<<”(“<

include

using namespace std;
class complex
{public:
complex(){real=0;imag=0;}
complex(double r,double i):real(r),imag(i){}
void display();
friend complex operator+(complex c1,complex c2); //在类中声明为友元函数 ,可以访问类中的私有成员
private:
double real;
double imag;
};
void complex::display()
{cout<<”(“<

include

using namespace std;
class Time
{public:
Time(){minute=0;sec=0;}
Time(int m,int s):minute(m),sec(s){}

Time operator++()   //++前置重载运算符 
{if (++sec>=60)     //秒数满60进1分 ,这里在执行 if语句时,sec已经加了1, 
    {sec-=60;
    ++minute;}
    return *this;   //返回当前对象值 
}
Time operator++(int)    //++后置重载运算符 
{Time temp(*this);
sec ++;
if(sec>=60)
{sec-=60;
++minute;}
return temp;
}
void display(){cout<<minute<<":"<<sec<<endl;}

private:
int minute;
int sec;
};
int main()
{Time time1(0,0),time2;
time1.display();
time2=++time1;
time1.display();
time2.display();

Time time3(0,0),time4;
time3.display();
time4=time3++;
time3.display();
time4.display();
return 0;
}
10.7 重载流插人运算符“<<”和流提取运算符“>>”
10.7.1 重载流插人运算符“<<”
cin和cout分别是istream类和ostream类的对象,c++编译系统已经对“>>” 和“<<”进行了重载,使之作为流插入运算符和流提取运算符,能用来输入和输出c++标准类型的数据。对于用户自己定义的类型的数据,是不能直接用“>>” 和“<<”来进行输入和输出的,如果想用它们输出和输入自己声明的数据的类型,必须对它们重载。
对“>>” 和“<<”重载的函数形式如下:
istream & operator>>(istream &,自定义类&);
ostream & operator<<(ostream &,自定义类&);
重载运算符 “>>” (“<<”)的函数的第一个参数和函数类型都必须是istream&(ostream&)类型【即istream类对象的引用,如对象“cin”】,第二个参数是要进行输入(输出)操作的类。只能将重载“>>” 和“<<”的函数作为友元函数,而不能将他们定义为成员函数,是因为成员函数只能引用本类成员和作用域内的数据。
10.7.2 重载流提取运算符“>>”
10.7.2.1.1.1 重载“>>” 和“<<”的示例

include

using namespace std;
class complex
{public:
complex(){real=0;imag=0;}
complex(double r,double i):real(r),imag(i){}
friend istream&operator >>(istream&,complex&);
friend ostream&operator<<(ostream&,complex&);
public:
double real;
double imag;
};
istream&operator>>(istream&,complex&c) //与教材不一致,感觉更加方便和好理解
//istream&operator>>(istream&input,complex&c) //教材的方法
{cin>>c.real>>c.imag;
return cin; //作用是为了可以连续向输入流插入信息
}
ostream&operator<<(ostream&,complex&c)
//ostream&operator<<(ostream&output,complex&c) //教材的方法
{ cout<<”(“<

include

using namespace std;
class complex
{public:
complex(){real=0;imag=0;} //定义有默认参数的构造函数
complex(double r,double i):real(r),imag(i){} //定义构造函数,用参数列表初始化
complex(double r){real=r;imag=0;} //定义转换构造函数
operator double(){return real;} //定义类型转换函数
friend istream&operator >>(istream&,complex&);
friend ostream&operator<<(ostream&,complex&);
private:
double real;
double imag;
};
istream&operator>>(istream&,complex&c) //与教材不一致,感觉更加方便和好理解
//istream&operator>>(istream&input,complex&c) //教材的方法
{cin>>c.real>>c.imag;
return cin; //作用是为了可以连续向输入流插入信息
}
ostream&operator<<(ostream&,complex&c)
//ostream&operator<<(ostream&output,complex&c) //教材的方法
{ cout<<”(“<

include

include

using namespace std;
class people //声明people类
{public:
void get_value()
{cin>>name>>sex>>age;}
void display()
{cout<

include

include

using namespace std;
class people //声明people类
{public:
//people(string nam,char s,int ag):name(nam),sex(s),age(ag){}
void get_value()
{cin>>name>>sex>>age;}
void display()
{cout<

include

include

using namespace std;
class people //声明people类
{public:
people(string nam,char s,int ag):name(nam),sex(s),age(ag){}
void display()
{cout<

include

include

using namespace std;
class people //声明people类
{public:
people(string nam,char s,int ag):name(nam),sex(s),age(ag){}
void display()
{cout<

include

include

using namespace std;
class people //声明people类
{public:
people(string nam,char s,int ag):name(nam),sex(s),age(ag){}
void display()
{cout<

include

include

using namespace std;
class people //声明people类
{public:
people(string nam,char s,int ag):name(nam),sex(s),age(ag){}
void display()
{cout<

include

include

using namespace std;
class people //声明people类
{public:
people(string nam,char s,int ag):name(nam),sex(s),age(ag){}
public:
string name;
char sex;
int age;
};
class student //声明类student
{public:
student(string nam,int nu):name(nam),num(nu){}
public:
string name;
int num;
};
class xueshengganbu:public people,public student //声明people、student的多重继承的派生类
{public:
xueshengganbu(string nam,char s,int ag,string nam1,int nu,string zw):people( nam, s, ag),student(nam1,nu),zhiwu(zw){}

public:
string zhiwu;
};
int main()
{xueshengganbu st1(“yang”,’f’,18,”li”,10086,”班长”); //定义派生类student的对象st1
cout<

include

include

using namespace std;
class people //声明people类为公共基类
{public:
people(string nam,char s,int ag):name(nam),sex(s),age(ag){}
void display()
{cout<

include

include

using namespace std;
class people //声明people类
{public:
people(string nam,char s,int ag):name(nam),sex(s),age(ag){}
void display()
{cout<

#include<iostream>
#include<string>
using namespace std;
class people    //声明people类 
{public:
    people(string nam,char s,int ag):name(nam),sex(s),age(ag){}     
    void display()
    {cout<<name<<":"<<sex<<" "<<age<<endl;}  
protected:
    string name;
    char sex;
    int age;
};
class birthdate
{public:
    birthdate(int y,int m,int d):year(y),month(m),day(d){}
void display_1()
    {cout<<year<<":"<<month<<" "<<day<<endl;}       
public:
    int year;
    int month;
    int day;
} ;

class student:public people     //以public方式声明people类的派生类student 
{public:
    student(string nam,char s,int ag,int y,int m,int d,int nu):people(nam,s,ag),bir(y,m,d),num(nu){}    
    void display_1()
    {display();
    cout<<bir.year<<" "<<num<<endl;}
public:
    birthdate bir;  //birthdate类的对象bir作为数据成员 
    int num;
};
int main()
{student st1("zhang",'f',888,2018,12,04,10086);
st1.display_1();
}

12 多态性与虚函数
12.1 多态性的概念
多态性是面向对象程序设计的一个重要特征,多态性就是向不同的对象发送同一个消息,不同的对象在接收时会产生不同的行为(即方法),也就是说每个对象可以用自己的方式去相应共同的消息。
c++多态性表现形式之一就是,具有不同功能的函数可以用同一个函数名,这样就可以实现用一个函数名调用不同内容的函数。多态性分为静态多态性和动态多态性。静态多态性就是函数的重载实现的或者运算符的重载(其实也是函数的重载)(前面已经叙述过),在编译时就知道调用函数的全部信息。
动态编译是不在编译时确定调用的是哪个函数,而是在程序运行中才动态的确定操作所针对的对象。动态多态性是通过虚函数实现的。
12.2 一个典型的例子
12.2.1.1.1.1 声明基类Point

include

using namespace std;
class Point
{public: //以下为声明公开的函数
Point(float x=0,float y=0); //声明有默认参数的构造函数
void setPoint(float,float); //声明成员函数
float getX() const {return x;} //定义成员函数
float getY() const {return y;} //定义成员函数
friend ostream &operator<<(ostream&,const Point&); //声明友元 运算符重载函数
protected: //以下为声明受保护的数据成员
float x,y;
};
Point::Point(float a,float b) //类外定义构造函数
{x=a;y=b;}
void Point::setPoint(float a,float b) //类外定义成员函数
{x=a;y=b;}
ostream &operator<<(ostream &output,const Point &P) //类外定义运算符重载函数(“<<”、“>>”只能定义为友元函数)
{output<<”[“<

#include <iostream>
using namespace std;
class point
{public:    //以下为声明公开的函数  
    point(float a,float b)  //声定义构造函数 
        {x=a;y=b;}
    void setPoint(float a,float b)      //定义成员函数 
        {x=a;y=b;}
    float getX() const  {return x;}     //定义成员函数 
    float getY() const  {return y;}     //定义成员函数
    friend ostream &operator<<(ostream&,const point&p);     //声明友元 运算符重载函数 
protected:  //以下为声明受保护的数据成员 
    float x,y;
};
     ostream &operator<<(ostream&,const point&p)    //类外定义运算符重载函数("<<"、">>"只能定义为友元函数)   
        {cout<<"点的坐标["<<p.x<<","<<p.y<<"]"<<endl;
        return cout;
        }

class circle:public point       //声明派生类 circle
    {public:
        circle(float a,float b,float r):point(a,b),radius(r){}  //定义构造函数 
        void setradius(float r)     //定义设置半径函数  
        {radius=r;}
        float getradius(){return radius;}
        float area() const      //定义函数求圆面积函数 
        {return 3.14*radius*radius;}
        friend ostream &operator<<(ostream&,const circle&c); 
    protected:
        float radius;
    };
    ostream &operator<<(ostream &,const circle &c)  //类外定义运算符重载函数("<<"、">>"只能定义为友元函数)   
        {cout<<"圆心坐标=["<<c.x<<","<<c.y<<"],半径="<<c.radius<<",面积="<<c.area()<<endl;
        return cout;
        }       
int main()
    {
    point p(2,2);   //建立 Point类对象p,并对其初始化
    cout<<"x="<<p.getX()<<",y"<<p.getY()<<endl;
    p.setPoint(8.5,6.8);
    cout<<p<<endl;

    circle c(2,2,2);        //建立circle对象,并初始化 
    c.setradius(4);
    cout<<"圆的参数:x="<<c.getX()<<",y="<<c.getY()<<",r="<<c.getradius()<<endl;
    cout<<c<<endl;  //调用用算符<<重载函数 
    }

12.2.1.1.1.3 声明circle的派生类cylin#include
using namespace std;
class point
{public://以下为声明公开的函数
point(float a,float b) //声定义构造函数
{x=a;y=b;}
void setPoint(float a,float b)//定义成员函数
{x=a;y=b;}
float getX() const{return x;}//定义成员函数
float getY() const{return y;}//定义成员函数
friend ostream &operator<<(ostream&,const point&p); //声明友元 运算符重载函数
protected://以下为声明受保护的数据成员
float x,y;
};
ostream &operator<<(ostream&,const point&p)//类外定义运算符重载函数(“<<”、”>>”只能定义为友元函数)
{cout<<”点的坐标[“<

#include<iostream>
#include<string>
using namespace std;
class people//声明people类 
{public:
people(string nam,char s,int ag):name(nam),sex(s),age(ag){}
virtual void display()//声明虚函数display
{cout<<name<<":"<<sex<<" "<<age<<endl;} 
protected:
string name;
char sex;
int age;
};

class student:public people//以public方式声明people类的派生类student 
{public:
student(string nam,char s,int ag,int nu):people(nam,s,ag),num(nu){}
virtual void display()//声明虚函数display
{people::display();//调用people类的display函数 
cout<<num<<endl;}
public:
int num;
};
int main()
{people p1("li",'m',18);
student st1("zhang",'f',19,10086);

people *pt=&p1;
pt->display();
pt=&st1;
pt->display();
}

3.2 静态关联与动态关联
函数的重载和通过对象名调用的虚函数,在编译时即可确定其调用的虚函数属于哪一类,其过程称为静态关联。通过指针的指向调用虚函数的过程是在运行阶段确定调用对象的,称为动态关联。
12.3.3 在什么情况下应当声明虚函数
a) 只能用virtual声明类的成员函数为虚函数,而不能将类外的普通函数声明为虚函数。
b) 一个成员函数被声明为虚函数后,在同一类族中的类就不能再定义一个非virtual但与该函数类型和参数相同的同名函数。
12.3.4 虚析构函数
一般习惯声明析构函数为虚析构函数,以保证在撤销动态分配空间时能得到正确处理。
12.4 纯虚函数与抽象类
12.4.1 纯虚函数
声明的一般形式:
virtual 函数类型 函数名(参数列表)=0;
纯虚函数是在声明虚函数时被“初始化”为0的函数,没有函数体,后面的0并不是返回值为0,只起形式上的作用。
纯虚函数只有函数名而不具备函数功能,不能被调用,是留给派生类定义时使用的。如果在派生类中没有对该函数定义,则仍然为纯虚函数。
12.4.2 抽象类
在面向对象程序设计中,有一些类,他们不用来生成对象,定义这些类的目的就是用它作为基类去建立派生类。
13 输入输出流
13.1 c++的输入和输出
13.1.1 输入输出的含义
c++的输入输出包括3个方面的内容,标准I/O,文件I/O,串I/O。
13.1.2 c++的I/O对c的发展—类型安全和可扩展性
c语言采用函数实现输入输出(如scanf和printf函数),c++采用类对象来实现输入和输出(如cin和cout),前者只能输入和输出标准类型的数据,且不能检查一些数据类型合法性,而后者提供了一套面向对象的输入输出系统,在编译时系统会对数据类型进行严格检查,是类型安全的。
13.1.3 c++的输入输出流
c++的I/O库中的类称为流类,用流类定义的对象称为流对象。
13.1.3.1 c++的流库
表13.1 p403
13.1.3.2 与流类库有关的头文件
流类库中不同的类的声明被放在不同的头文件中,用户在使用类时应将对应的头文件用#include包含在程序中,具体包含见后面的介绍。
13.2 标准输入输出流(标准I/O)
13.2.1 标准输出流
标准输出流是流向标准输出设备(显示器)的数据。
13.2.1.1 cout,cerr和clog流
cout,cerr和clog流都是在iostream头文件中声明的类。cout流通常是传送到显示器输出,但也可以被重新定向输出到磁盘文件,而cerr流中的信息只能在显示器输出,因为当调试程序时,往往不希望运行出错的信息被送到其他文件,而要求及时在显示器输出。
clog和cerr作用相同,区别是cerr不经过缓冲器直接向显示器上输出有关信息,而clog中的信息存放在缓冲区,缓冲区满后或遇到endl时向显示器输出。
再用cout输出时,如果出现屏幕关闭,看不到输出的结果可以在return 0语句前加 system(“PAUSE”);(Win3控制台应用程序)或Console::ReadLine();(CLR控制台项目)。

13.2.1.1.1.1 示例

include

include

using namespace std;
int main()
{
int a;
cin>>a;
if (a>2) cerr<<”数据不符合要求”;
else cout<<”数据符合要求”;
}
13.2.1.2 标准类型数据的格式输出
13.2.1.2.1 使用控制符控制输出格式
13.2.1.2.2 使用流对象的成员函数控制输出格式
表13.3
表13.4
13.2.1.2.2.1 示例

include

include

include

using namespace std;
int main()
{
int a=21;
cout.setf(ios::showbase); //输出整数的基数
cout<<”dec:”<

include

using namespace std;
int main()
{
cout.put(‘a’);
cout<

include

include

using namespace std;
int main()
{
int c;
c=cin.get();
cout<

include

using namespace std;
int main()
{
int c;
while((c=cin.get())!= EOF)
cout.put(c);
}
13.2.2.2.2 有1个参数的get函数
调用形式:
cin.get(ch);
作用是从输入流中读取一个字符,赋值字符变量ch,如果读取成功函数返回值为非0,否则返回值为0。
13.2.2.2.3 有3个参数的get函数
调用形式:
cin.get(字符数组(或字符指针),字符个数n,终止字符);
作用是从输入流中读取n-1个字符,然后加一个’\0’共n个字符赋给指定的字符数组(或字符指针指向的数组),如果在读取n-1个字符之前遇到指定的终止字符,则读取提前结束。如果读取成功函数返回值为非0,否则返回值为0。
13.2.2.2.3.1 用getline函数读入一行字符
cin.getline(字符数组(或字符指针),字符个数n,终止标志字符);
作用是读取n-1个字符(或遇到指定的终止字符),然后加一个’\0’共n个字符放到字符数组中。与有3个参数的get函数类似。
13.2.2.2.3.2 getline函数示例

include

using namespace std;
int main()
{
char ch[20];
cin.getline(ch,20,’/’);
cout<

include

using namespace std;
int main()
{char c;
cout<<”请输入字符串:”<

#include<iostream>
using namespace std;
int main()
    {char c[20];
    int ch;
    cout<<"请输入一个句子:"<<endl;
    cin.getline(c,15,'/');
    cout<<"第一部分是:"<<c<<endl;
    ch=cin.peek();
    cout<<"下一个字符是:"<<ch<<endl;
    cin.putback(c[0]);
    cin.getline(c,15,'/');
    cout<<"第二部分是:"<<c<<endl; 
    return 0;
    }

13.2.2.7 ignore函数
调用形式:
cin.ignore(n,终止字符);
作用是跳过输入流中n个字符,或在遇到指定的终止字符时提前结束(此时跳过包含终止字符在内的若干字符)。
13.2.2.7.1.1 ignore函数的用法

#include<iostream>
using namespace std;
int main()
    {char ch[20];
    cin.get(ch,10,'/');
    cout<<"第一部分是:"<<ch<<endl;
    cin.ignore(ch,10,'/');  //跳过输入流中10个字符,或在遇到指定的终止字符时提前结束
    cout<<"下一部分是:"<<ch<<endl; 
    return 0;
    }

13.3 对数据文件的操作与文件交流(文件I/O)
13.3.1 文件的概念
文件一般是指存储在外部介质上的数据的集合,一批数据是以文件的形式存放在外部介质上的,操作系统是以文件为单位对数据进行管理的。根据文件中数据的组织形式,可分为ASCII文件和二进制文件。ASCII文件又称为字符文件,二进制文件又称为字节文件。
13.3.2 文件流类与文件流对象
文件流是以外存文件为输入输出对象的数据流。c++的输入输出是由流类对象来实现的。其实在用标准设备为对象的输入输出中,也是要定义流对象的,如cin、cout就是流对象,由于cin、cout已在iostream头文件中实现定义,所有用户不需要定义,可直接使用,但是在用文件输入输出时,情况复杂,事先无法统一定义,故必须留给用户根据需要自行定义。
13.3.3 文件的打开与关闭
此处文件的打开关闭并非将文件真正的打开关闭,二是将文件流对象和文件建立关联和取消关联,用以对文件进行输入输出操作。
13.3.3.1 打开磁盘文件
有两种方法可以打开文件
13.3.3.1.1 调用文件的流成员函数open可打开文件,一般形式:
文件流对象.open(磁盘文件名,输入输出方式);
磁盘文件名可以包括路径,如果缺省路径,则默认为当前目录
ofstream outfile; //定义outfile流类的对象outfile
outfile.open(“f1.dat”,ios::out) //利用对象outfile调用open函数打开文件f1.dat(使文件流outfile与文件f1.dat关联),并指定为输出方式
13.3.3.1.2 在定义文件流对象时指定参数
在声明文件流类时定义了带参数的构造函数,其中包括了打开磁盘文件的功能,因此可以在定义文件流对象时指定参数,调用文件流类的构造函数来实现打开文件的功能,如:
ofstream outfile(“f1.dat”,ios::out);
一般多用此方式,比较方便,与open函数作用相同。
表13.5文件输入输出方式设置值
方式 作用
ios::in 以输入方式打开文件
ios::out 以输出方式打开文件(默认方式),如果文件已存在,则将其原有内容全部删除,如果不存在则建立新文件(前提是不包含ios::in、ios::app、ios::ate)
ios::app 以输出方式打开文件,写入的数据添加在文件末尾
ios::ate 打开一个已有文件,文件指针指向文件末尾
ios::trunc 打开一个文件,如果文件已存在,则删除其中全部数据,如果不存在则建立新文件
ios::binary 以二进制方式打开一个文件,如果不指定此方式则默认为ASCII方式
ios::nocreate 打开一个已有文件,如果文件不存在,则打开失败,nocreate意思是不建立新文件
ios::noreplace 如果文件不存在则建立新文件,如果已存在则操作失败,noreplace意思是不更新原有文件
ios::in|ios::out 以输入和输出方式打开文件,文件可读可写
ios::out|ios::binary 以二进制方式打开一个输出文件
ios::in|ios::binary 以二进制方式打开一个输入文件

说明:
1. 每打开一个文件都有一个文件指针,该指针的初始位置由I/O方式指定,每次读写都是从文件指针的当前位置开始,每读入一个字节,指针就向后移动一个字节,当文件指针移动到最后,就会遇到文件结束符(值为-1)此时流对象的成员函数eof的值为非0值,表示文件结束。
2. 可以用“位或”运算符“|”对输入输出方式进行组合,但不能组合相互排除的方式。
3. 如果打开文件操作失败,open函数的返回值为0,如果是用调用构造函数的方式打开文件的,则流对象的值为0,可以据此测试打开是否成功。
13.3.3.2 关闭磁盘文件
对已打开的磁盘文件读写操作完成后,应关闭文件(取消关联),用流对象调用文件的流成员函数close可关闭打开的文件。用法:
文件流对象. close();
outfile.close(); //关闭打开的文件
13.3.4 对ASCII文件的操作
对ASCII文件的读写操作可以用两种方法:
1. 用流插入运算符“<<”和流提取运算符“>>”。
2. 用文件流类的put、get、geiline等成员函数进行字符的输入输出。
13.3.4.1.1.1 打开(创建)文件并输入字符

#include<iostream>
#include<fstream>   //创建文件流类ofstream对象需

要包含的头文件

using namespace std;
int main()
{   
    int a[10]; 
    ofstream outfile("f1.dat",ios::trunc);  //定义文件流对象,打开(或创建)磁盘文件f1.dat,如果打开失败则流对象返回0值
    if(!outfile)        //如果上述返回值为0,则提示文件打开失败
        {cerr<<"文件打开失败!"<<endl;
        exit(1);    //调用系统函数结束运行 
         } 
        cout<<"输入10个数:"<<endl;
        for(int i=0;i<10;i++)       //给数组赋值,并将数组的值写入打开的文件
            {cin>>a[i];
            outfile<<a[i]<<" ";
             } 
    outfile.close();        //关闭打开的文件
    return 0;
}

13.3.4.1.1.2 读取文件中的数值找出最大值

#include<iostream>
#include<fstream>   //创建文件流类ofstream对象需

要包含的头文件

using namespace std;
int main()
{   
    int a[10]; 
    ifstream infile("f1.dat",ios::in);  //定义输入文件流对象,以输入方式打开文件f1.dat,如果打开失败返回0值 
    if(!infile)     //如果上述返回值为0,则提示文件打开失败 
        {cerr<<"文件打开失败!"<<endl;
        exit(1);    //调用系统函数结束运行 
         } 
        cout<<"文件中前10个数如下:"<<endl;
        for(int i=0;i<10;i++)   //读取文件中的内容放入数组 
            {infile>>a[i];
            cout<<a[i]<<endl;
             } 
    int max,order;
    max=a[0];
    order=0;
    for(int i=1;i<10;i++)
        if(a[i]>max)
        {max=a[i];
        order=i+1;
        }
    cout<<"max="<<max<<endl<<"数组中第"<<order<<"个数是最大的数"<<endl; 
    infile.close();
    return 0;
}

13.3.5 对二进制文件的操作
对二进制文件的操作也需要先打开文件,用完后要关闭文件,再打开时要用ios::binary指定为以二进制形式传送和储存。二进制文件除了作为输入、输出文件外,还可以是既能输入又能输出的文件。
13.3.5.1 用成员函数read和write读写二进制文件
对二进制文件的读写主要用istream类的成员函数read和write来实现,这两个成员函数的原型为:
istream& read(char *buffer,int len);
ostream& write(const char *buffer,int len);
字符指针指向内存中一段存储空间,len是读写的字节数,调用方式为:
a. read(p,10);
作用是字符指针p指向的单元开始的10个字节的内容不加转换的写到与a关联的文件中。
b. write(p,10);
作用是从b所关联的文件中,读取10个字节的内容放到字符指针指针p指向的空间内。
13.3.5.1.1.1 二进制文件作为输入输出文件

#include<iostream>
#include<fstream>   //创建文件流类ofstream对象需要包含的头文件 
#include<string>
using namespace std;
int main()
{   //以二进制形式给文件写入数据 
    ofstream outfile("f1.dat",ios::binary); //指定以二进制方式打开文件f1.dat,如果打开失败返回0值 
    if(!outfile)        //如果上述返回值为0,则提示文件打开失败 
        {cerr<<"文件打开失败!"<<endl;
        exit(1);    //调用系统函数结束运行 
         } 
    char ch[5];
    for(int i=0;i<5;i++)
        cin>>ch[i];
    outfile.write(ch,sizeof(ch));   //用成员函数write向磁盘文件写入数据 
    outfile.close();
    //以二进制形式读取文件数据 
    ifstream infile("f1.dat",ios::binary);  //指定以二进制方式打开文件f1.dat,如果打开失败返回0值 
    if(!infile)     //如果上述返回值为0,则提示文件打开失败 
        {cerr<<"文件打开失败!"<<endl;
        exit(1);    //调用系统函数结束运行 
         } 
    char ch1[5];
    infile.read(ch1,sizeof(ch1));   //用成员函数read读取文件数据并写入数组ch1 
    cout<<ch1;
    outfile.close();        
    return 0;
}

13.3.5.2 与文件指针有关的流成员函数
在磁盘中有一个文件读写位置标记来指明当前应进行读写的位置,在从文件输入(输出)每一个字节,该位置就向后移动一个字节。对于二进制文件,允许对位置标记进行控制,使得按用户的意图移动到所需的位置,以便在该位置上进行读写。一般情况下读写是顺序进行的,但对二进制数据文件来说,可以利用下面的成员函数移动指针,随机的访问修改文件中任一位置上的数据。
表13.6 文件流与文件位置标记有关的成员函数
成员函数 作用
gcout() 得到最后一次输入所读入的字节数
tellg() 得到输入文件位置标记的当前位置
tellp() 得到输出文件位置标记的当前位置
seekg(文件中的位置) 将输入文件位置标记移到指定位置
seekg(位移量,参照位置) 以参照位置为基础移动若干字节
seekp(文件中的位置) 将输出文件位置标记移到指定位置
seekp(位移量,参照位置) 以参照位置为基础移动若干字节

说明:参照位置可以是ios::beg(文件开头,是默认值),ios::cur(位置标记当前位置),ios::end(文件末尾),它们是在ios类中定义的枚举常量
13.3.5.2.1.1 随机访问二进制数据文件

#include<iostream>
#include<fstream>   //创建文件流类ofstream对象需要包含的头文件 
#include<string>
#include <cstring>  //使用strcpy函数需要包含的头文件 
using namespace std;
struct student
    {int num;
    char name[20];
    float score;
     } ;
int main()
{   student st[5]={1001,"li",89,1002,"zhang",90,1003,"zhao",91,1004,"ta",92,1005,"gen",93}; 
    fstream iofile("f1.dat",ios::in|ios::out|ios::binary);  //指定以二进制读写方式打开文件f1.dat,如果打开失败返回0值 
    if(!iofile)     //如果上述返回值为0,则提示文件打开失败 
        {cerr<<"文件打开失败!"<<endl;
        abort();    //调用系统函数结束运行,与exit(1)一样 
         }
    for(int i=0;i<5;i++)    //向磁盘写入5个学生的数据 
        iofile.write((char*)&st[i],sizeof(st[i]));
    student st1[5];
    for(int i=0;i<5;i+=2)
        {iofile.seekg(i*sizeof(st[i]),ios::beg);    //定位于第0,2,4学生数据开头
        iofile.read((char*)&st1[i/2],sizeof(st1[0]));   //先后读入3个学生的数据,存放在st1[0], st1[1], st1[2] 
        cout<<st1[i/2].num<<" "<<st1[i/2].name<<" "<<st1[i/2].score<<endl;      //输出st1[0], st1[1], st1[2]的值 
        }
    cout<<endl<<endl;
    st[2].num=1106;     //修改第三个学生的数据
    strcpy(st[2].name,"wueee");
    cout<<st[2].name<<endl<<endl;
    iofile.seekg(2*sizeof(st[0]),ios::beg);     //定位于第3个学生数据的开头
    iofile.write((char*)&st[2],sizeof(st1[2])); //更新第三个学生数据
    iofile.seekg(0,ios::beg);   //重新定位于文件开头
    for(int i=0;i<5;i++)
        {iofile.read((char*)&st[i],sizeof(st[i]));  //读入5个学生数据
        cout<<st[i].num<<" "<<st[i].name<<" "<<st[i].score<<endl;
         }      
    iofile.close();     
    return 0;
}

13.4 字符串流(串I/O)
文件流是以外存文件为输入输出对象的数据流,字符串流不是以外存文件为输入输出的对象,二是以内存中用户定义的字符数组(字符串)为输入输出的对象,即将数据输出到内存中的字符数组,或者从字符数组将数据读入,字符串流也称内存流。字符数组中可以存放字符,也可以存放整数、浮点数以及其他类型的数据。
字符串流类包含istrstream、ostrstream和strstream类,这三个类是在头文件strstream中定义的,调用时需要包含该头文件。
13.4.1 建立输出字符串流对象
ostrstream类提供的构造函数的原型:
ostrstream::ostrstream(char*buffer,int n,int mode=ios::out);
buffer是指向字符数组首元素的指针,n为指定的流缓冲区的大小(可以与字符数组相同,也可以不同),第三个参数是可选的,默认为ios::out方式。可以用下列语句建立输出字符串流对象并与字符数组建立关联:
ostrstream strout(ch,20)
作用是建立输出字符串流对象strout,并使strout与字符数组ch关联,通过字符串流将数据输出到字符数组ch中,流缓冲区大小为20。
13.4.2 建立输入字符串流
istrstream类提供的构造函数原型有两个:
istrstream:: istrstream(char*buffer);
istrstream:: istrstream(char*buffer,int n);
buffer是指向字符数组首元素的指针,n为指定的流缓冲区的大小。可以用下列语句建立输入字符串流对象并与字符数组建立关联:
istrstream strin(ch);作用是建立输入字符串流对象,将字符数组ch中的全部数据作为输入字符串流的内容。
istrstream strin(ch,20);
流缓冲区大小为20,因此只能将字符数组ch中的前20个字符作为输入字符串流的内容。
13.4.3 建立输入输出字符串流对象
strstream类提供的构造函数的原型:
strstream:: strstream(char*buffer,int n,int mode);
可以用以下语句建立输入输出字符串流对象:
strstream strio(ch,sizeof(ch),ios::in|ios::out);
作用是以字符数组ch为输入输出对象,流缓冲区大小与数组ch相同。
13.4.3.1.1.1 将一组数据用ostrstream类的对象保存在数组中

#include<iostream>
#include<strstream>
using namespace std;
struct student
    {int num;
    char name[20];
    float score;
     } ;
int main()
    {student st[3]={1001,"li",89,1002,"zhang",90,1003,"zhao",91}; 
    char c[50];
    ostrstream strout(c,30);    //建立输出字符串流,与数组c建立关联,缓冲区长为30 
    for(int i=0;i<3;i++)    //向字符数组c写3个学生的数据 
        strout<<st[i].num<< st[i].name<<st[i].score;
        strout<<ends;   //ends是c++的I/O操作符,插入一个'\o' 
        cout<<"数组c:"<<c<<endl; 
    }

13.4.3.1.1.2 读取一个数组的数据用istrstream类的对象写入另一个数组中

#include<iostream>
#include<strstream>
using namespace std;
int main()
    {char c[50]="12 34 65 -23";
    int a[10],i,j,t;
    cout<<"数组c:"<<c<<endl;  //显示字符数组中的字符串
    istrstream strin(c,sizeof(c));  //建立输入串流对象strin并与字符数组c关联
    for(i=0;i<4;i++)    //从字符数组c读取4个整数赋给整形数组a 
        strin>>a[i];
    cout<<"数组a:";
    for(i=0;i<4;i++)
        cout<<a[i]<<" ";    //显示整形数组a各元素
    cout<<endl;
    return 0;
    }

14 c++工具
c++编译系统根据实际需要,增加了一些功能,作为工具来使用,主要有模板(包括函数模板和类模板,见4.9和9.11节)、异常处理、命名空间和运行时类型识别。
14.1 异常处理
所谓异常处理指的是对运行时出现的差错以及其他例外情况的处理。
14.1.1 异常处理的任务
在程序设计时,应当事先分析程序运行时出现的各种意外情况,并且分别制定出相应的处理方法,这就是程序的异常处理任务。
14.1.2 异常处理的方法
如果在执行一个函数过程中出现异常,可以不再本函数立即处理,而是发出一个信息,传递给它的上一级,它的上级捕捉到这个信息后可以进行处理,如果不能处理,再传给其上一级处理,,如此逐级上送,如果最上级还无法处理,最后只能异常终止程序的执行。这样的做法使得异常的发现和处理不必由同一函数来完成,好处是使底层的函数专门用于解决实际任务,而不必再承担异常处理任务,减少负担。例如在主函数中调用十几个函数,只需在主函数中设置异常处理即可,可以提高程序效率。
c++处理异常的机制是由3个部分组成的:检查(try),抛出(throw)和捕捉(catch)。把需要检查的语句放在try块中,throw用来当出现异常时发出一个异常信息(形象的称为抛出),而catch则用来捕捉异常信息,如果捕捉到了就进行处理。
一般语法:
try
{被检查的语句 throw 变量}
catch(异常信息类型[变量名])
{进行异常处理的语句}
14.1.2.1.1.1 简单的异常处理示例

#include<iostream>
#include<cmath>
using namespace std;
int main()
    {double triangle(double,double,double);
    double a,b,c;
    cout<<"请输入三角的边长"<<endl;
    cin>>a>>b>>c;
    try
    {while(a>0&&b>0&&c>0)
        {cout<<triangle(a,b,c)<<endl;
        cout<<"请继续输入三角的边长或者退出"<<endl;
        cin>>a>>b>>c;
        }
    }
    catch(double)   //用catch捕捉异常信息并进行处理
        {cout<<"a="<<a<<",b="<<b<<",c="<<c<<"这3个边不能构成三角形"<<endl;
        }
    cout<<"结束"<<endl;
    system("PAUSE");    
    return 0;
    }

    double triangle(double a,double b,double c)
        {double s=(a+b+c)/2;
        if (a+b<=c||b+c<=a||c+a<=b) throw a;    //当不符合三角形条件抛出异常信息a
        return sqrt(s*(s-a)*(s-b)*(s-c));
        }

说明:
1.被检测的部分必须放在try块中,否则不起作用
2.try块和catch块作为一个整体出现,catch块是try-catch结构中的一部分,必须紧跟在try之后,不能单独使用,在二者之间也能插入其他语句。
3.try块和catch块中必须有用花括号括起来的复合语句
4.一个try-catch结构中只能有一个try块,但可以有多个catch块,以便与不同的信息匹配。
try
{ ……
}
catch(double) //用catch捕捉异常信息并进行处理
……
catch(int) //用catch捕捉异常信息并进行处理
……
5.catch后面的圆括号一般只写异常信息的类型,但还可以写变量名,这样可以将throw抛出的变量的值赋给此变量,得到throw抛出的值。
6.如果在catch字句中没有指定异常的类型,而用删节号“…”表示,则表示它可以捕捉所有类型的异常信息
7.在某些情况下,在throw语句中可以不包括表达式,如果在catch块中包含throw表示“我不处理这个异常,请上级处理”。此时catch块把当前的异常信息再次抛出,给上一级的catch处理。
9.如果throw抛出的异常信息找不到与之匹配的catch块,那么系统就会调用一个系统函数terminate,使程序终止运行。
10. try-catch结构可以与throw出现在同一个函数中,也可以不在同一个函数中。当throw抛出异常信息后,首先在本函数中寻找与之匹配的catch,如果在本层无try-catch结构或找不到与之匹配的try-catch结构,就转到上一层去处理,如果还找不到就继续向上级转,也就是说,转到离开出现异常最近的try-catch结构中去处理。
14.1.2.1.1.2 函数嵌套的情况下检测异常处理

#include<iostream>
#include<cmath>
using namespace std;
int main()
    {void f1();
    try
        {f1();}
    catch(double)
        {cout<<"错误0"<<endl;}
    cout<<"结束0"<<endl;
    system("PAUSE");    
    return 0;
    }
void f1()
    {void f2();
    try
        {f2();}
    catch(char)
        {cout<<"错误1"<<endl;}
    cout<<"结束1"<<endl;
    }
void f2()
    {void f3();
    try
        {f3();}
    catch(int)
        {cout<<"错误2"<<endl;}
    cout<<"结束2"<<endl;
    }
void f3()
    {double a=0;
    try
        {throw a;}
    catch(double)
        {cout<<"错误3"<<endl;}
    cout<<"结束3"<<endl;
    }

运行结果如下:
错误3
结束3
结束2
结束1
结束0
请按任意键继续…
14.1.3 在函数声明中进行异常情况指定
为了便于阅读程序,使用户在看程序时能知道所用的函数是否会抛出异常信息以及异常信息的类型,c++允许在声明函数时列出可能抛出的异常类型,如:
double triangle(double,double,double) throw (int,double);
异常指定是函数声明的一部分,必须同时出现在函数声明和函数定义的首行中,如果在声明时未列出可能抛出的异常类型,则该函数不可以抛出任何类型的异常信息。

14.1.4 在异常处理中处理析构函数
如果在try块中定义了类对象,在执行try块的过程中如果发生了异常,此时流程离开try块,同时做好结束对象前的清理工作,即调用对象的析构函数,析构函数的执行与构造函数执行顺序相反。
14.2 命名空间
14.2.1 为什么需要命名空间
命名空间是ANSI c++引入的可以由用户命名的作用域,用来处理常见的同名冲突。在C语言中定义了3个层次的作用域,文件(编译单元)、函数、复合语句,c++又引入了类作用域,在不同的作用域中可以定义相同名字的变量,当然这只是在一个文件内讨论的。如果一个简单程序中包含少数几个文件,可以通过extern声明一个程序中多个文件中的同名变量是一个变量,只要小心可以避免出错。但在由多人合作完成的大型软件,不同的人完成不同的部分,这样就无法保证程序的正确性。
14.2.2 什么是命名空间
为了解决此问题ANSI c++引入了命名空间,实际上就是一个程序设计者命名的内存区域。程序设计者可以根据需要指定一些有名字的空间域,把一些全局实体分别放在各个命名空间,从而与其他全局实体分隔开,以免产生冲突。
可以根据需要设置多个命名空间,但不能重名。过去用的全局变量可以理解为全局命名空间,独立于所有命名空间之外。
用法与类的声明类似,区别在于定义命名空间时,右花括号后面没有分号。例如:
namespace ns1
{int a;
doubleb;
}
14.2.3 使用命名空间成员的方法
在引用命名空间成员时,要用命名空间名和作用域分辨符命名空间成员进行限定,以区别不同的命名空间中的同名标识符,如:
命名空间名::命名空间成员
这种方法是有效的,能保证所引用的实体有唯一名字,但如果命名空间名字较长,引用时要写很长名字,不太方便,为此,c++提供了一些机制能简化使用。
14.2.3.1 使用命名空间别名
可以为命名空间起一个别名来代替较长的命名空间名,如:
namespace Television
{int a;
double b;
}
……
Television::a;
可以用较短易记的别名代替,如;
namespace TV= Television //别名TV代替Television
TV::a;
14.2.3.2 使用using 命名空间成员名
using后面的命名空间成员必须是由命名空间限定的名字,如:
using ns1::student
以上语句声明,在本作用域内(using语句的作用域)会用到命名空间ns1中的成员student,在本作用域内如果使用该命名空间成员时,不必再逐个用命名空间限定。如:
cout<

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值