C++程序设计

╭第一章 C++函数和对象
|第二章 从结构到类的演变
|第三章 函数及函数模板
|第四章 类和对象
|第五章 特殊函数和成员
内容大纲<
|第六章 类的继承和派生
|第七章 类模板和向量
|第八章 多态性和虚函数
|第九章 运算符的重载及流类库
╰第十章 面向对象实际设计

第一章 C++函数和对象

1.混合性语言
C++以.cpp为文件扩展名,有且只有一个名为main的主函数,因保留了这个面向过程的函数,所以称为混合型语言;

2.注释方式: /*…*/,//…;
一个简单的C++程序
#include<iostream.h> //包含文件
void main(void){
cout<<“I am a student.\n”;//输出字符串
getchar();
cin>> 输入字符串;
}

3.输入输出对象
1>提取操作:用提取操作符">>”从cin输入流中提取字符,如 cin>>a.x;
2>插入操作:用插入操作符”<<”向cout输出流中插入字符,如 cout<<”We”;cout<<endl;
3>使用标准输入(键盘输入)cin及标准输出(屏幕输出) cout前,要在主函数前使用#include<iostream>
将C++标准输入输出库头文件iostream将其包括;
4>换行操作:用语句cout<<endl;或cout<< “\n”;实现,其中endl可以插在流的中间;
如:cout<<a.x<<endl<<a.y<<endl;

5>setw(n)是c++中在输出操作中使用的字段宽度设置,n表示字段宽度。
用该函数时必须用头函数名声明:#include<iomanip>进行声明

6>数据的简单的输入输出格式:
cout<<dec ;//转换为十进制输出;
cout<<oct ;//转换为八进制输出;
cout<<hex ;//转换为十六进制输出;
cout<<endl;//输出一个换行符并刷新流;

//以下4个操控符必须使用包含头文件#include<iomanip>
cout<<setw(n); //设置输出数据字段宽度
setiosflags(flag);//设置flag指定的标志位
resetiosflags(flag)//清除flag指定的标志位

cout<<width(n);//设置字段宽度为n,右对齐,如果字段位数不够n,那左边会填空格
cout<<fill("*"); //使用*填充空白部分

cout<<setprecision(n);//设置转换成浮点数输出,n就是保留有效数字的个数(不包括小数点),
不够的话有多少算多少

示例:
#include <iostream>
#include <iomanip>
using namespace std;
#define PI 3.1415926
void main() {
double a=3.14;
cout <<PI<<endl; //3.1415926
cout << setprecision(0)<<PI<<endl;//3
cout << setprecision(1)<<PI<<endl;//3
cout << setprecision(2)<<PI<<endl;//3.1
cout << setprecision(3)<<PI<<endl;//3.14
cout << setprecision(4)<<PI<<endl;//3.142
cout << setprecision(5)<<a<<endl;//3.14 //不够的话有多少算多少
}


#include<iostream>
#include<iomanip> //setw(n),n表示字段宽度
using namespace std;
void main(){
int a=29,b=100;
cout<<setw(3)<<a<<b<<endl;
cin.get();
}

4.使用命名空间
C++相比C而言,可以省略”.h”表示头文件,但必须使用语句using namespace std;来让命名空间中的对象名称曝光,
例:#include <iostream> //包含头文件;(#是指在编译阶段就要使用)
using namespace std;//使用命名空间

注意C++库中代替C库中的头文件的正确名称,例如下面两个语句效果一样:
*1.#include<math.h> //include 包含相当于java的import
*2.#include<cmath> //不带头文件 math.hcmath
using namespace std;//使用命名空间(省略头文件);

5.对象的定义和初始化
定义:对象包括为它命名并赋予它数据类型,一般即使初始值只用来表示该对象尚未具有真正意义的值,
也要将每个对象初始化; int z(0);//等同于C语言中int z=0;

6.函数原型及其返回值
①.在主函数中声明函数 int result(int,int);//除了默认参数(默认参数的默认值)和内联函数(函数体及其内语句)外,
不需给参数的变量名;
②.除构造函数(申请空间)与析构函数外(释放空间),函数都需要有类型声明;没有返回值用void标识,且不用return语句;

7.const(常量)修饰符及预处理程序
1>const修饰符:用于定义符号常量
*1.C中使用宏定义 #define定义常量,而C++中除此之外,建议使用const代替宏定义,用关键字const修饰的标识符称为符号常量
#define BUFSIZE 100 const int BUFSIZE 100;
#include <iostream.h>
#define PRICE 30//define宏定义,编译语句只是声明替换效果,不是语句,不能加分号.常量,在程序中保持不变;
//可以替换为const int PRICE=10
void main(void){
int num,total;
num=10;
total=num*PRICE;
cout<<”total=”<<total;//输出结果
}
*2.const也可以使用构造函数的初始化方法 const int k(2);//等同于const int k=2;
*3.一般const初始化时就要赋值,除非是外部变量,extern const int d;//外部变量, 用const来修饰,能提高程序的安全性,

2>对指针使用const限定符
*1.变量指针:就是变量的地址,
指向变量的指针变量;存放变量地址的变量是指针变量
指针变量指向的变量:*p代表指针变量指向的变量也就是变量a
int a=10;p=&a *p=*&a=a ;(*p)++=a++;

*2.指向常量的指针
const int y=58; const int *p1=&y; //const修饰的是int *(取内容,内容不变),指向常量的指针,y和*p值不变
*3.常量指针
int x=5; int *const p=&x; const修饰的p(取地址,地址不变) 指针指向的地址不变,&y和p的地址值不变;

*4.指向常量的常量指针
const int x=2;
const int * const p=&x; *p和p都是常量

8.预处理程序
①.预处理语句前加”#”为了区分一般的C++语句,作用是把所有出现的,被定义的名字全部替换成对应的”字符序列”;
②.预处理语句有三种:宏定义,文件包含(也称嵌入指令)和条件编译;(编译语言是将源程序翻译成机器语言);
③.文件包含是指一个程序把另一个指定文件的内容包含进去,书写时可以使用引号,也可以使用尖括号,
前者引用自己定义的包含文件,如:#include”E:\prog\myfile.h”,后者引用系统提供的包含文件,如标准输入输出是
定义在标准库iostream中的,引用是要包括以下两条语句:
#include<iostream> //包含头文件 系统提供的包含文件
using namespace std;//使用命名空间;
自己本地的路径#include "E:\prog\myfile.h" //预编译的时候可以用"\"
当进入编译阶段:string path="E:\\prog\\myfile.h";要用转义符
#define NUM (8*5)+(6*7)


9.程序书写格式(增加可读性)
1>括号紧跟函数名后面,但在for和while后面,应用一个空格与左右号隔开;
2>数学运算符左右各留一个空格;
3>在表示参数时,逗号后面留一个空格;
4>在for、do…while和while语句中,合理缩进,一对花括号和空行
5>适当增加空行和程序注释以增加可读性;
6>太长的程序分为两行或几行,并注意选取合适的分行和缩进位置;
7>变量名命名时长度不大于247个字符,不能用关键字,,大小写区分;向上兼容C程序

8>C++的关键字
if else switch case break default for continue do while return
static volatile void struct union extern const const_cast virtual
private public protected reinterpret_cat static_cast mutable friend
typedef unsigned signed enum float double int long char short bool
# ## #define #undef #error #if #elif #endif #ifdef #include #line
#pragma asm inline defined delete sizeof register typeid false true
auto goto class template new operator this try catch throw
using namespace


10.新的基本数据类型及注意事项
1>void是无类型标识符,只能声明函数的返回值类型,不能声明变量;
2>C++还比C多了bool(布尔)型, 1真 0:假
3>C++只是限定int和short至少要有16位,而long至少32位,short不得长于int, int不得长于long;
4>地址运算符 “&”用来取对象存储的首地址,对于数组,则数组名就是数组的首地址;
e.g int x=56;cout <<&x; 自动使用十六进制输出存储的首地址006AFDEC
5>C++中的常量分三种,
①.符号常量; #define PRICE 30 (宏定义)
②.整型常量(4种): 十进制, 长整型(后缀L) 八进制(前缀0), 十六进制(前缀0X)
③.浮点常量(3种): float型(f), long float型, double型;

* C++的基本数据类型包括字符类型,整数类型和逻辑类型;
* 字符型数据在内存中以ASCII吗的形式存放↓
* C++与C一样,也使用转义序列,如:’\0’表示ASCII码值为零的空字符(NULL),’\101’表示字符A.
* 逗号表达式运算:取逗号表达式中最后一个逗号表达式的值
示例:
a=3*5,a*4; a的值为15,表达式的值为60; //先执行赋值运算,再取最后一个逗号表达式的值
a=3*6,a*4,a+5; a的值为18,表达式的值为23; //同上

11.动态分配内存
1>在使用指针时,如果不使用对象地址初始化指针,可以自己给它分配地址,对于只存储一个基本类型数据的指针,
申请方式如下:
new 类型名[size]//申请可存储size个该数据类型的对象,不用时,必须用delete指针名;释放已申请的存储空间
如:……
double *p;//声明double型指针,本来double x=3.0,p=&x,但是现在没有x,要初始化p指针↓;
p=new double[3];//使用new分配3个double型数据的存储空间,这是指针p自己开辟空间
……..
delete []p;//释放已申请的存储空间;用delete将指针p是自己开辟的空间主动释放掉
……;
指针名=new 结构名;//分配
delete 指针名;//释放

12.引用
1>定义:引用就是为现有的对象起个别名,别名的地址与引用对象的地址是一样的;
2>引用的声明方式:
数据类型&别名=对象名; 注意对象在引用前必须先初始化,另外&的位置无关紧要;
int x=56; //定义并初始化x
int &a=x;//给x起了一个别名a,也就是a是x的引用,二者地址相同;
int &r=a;//声明r是a的引用,二者地址相同
r=25;//改变r.则a和x都同步变化;

[说明]:
*1.所谓”引用”,就是将一个新标识符和一块已经存在的存储区域相关联,因此,使用引用时没有分配新的存储区域
它本身不是新的数据类型.

*2.可以通过修改引用来修改原对象,但是不能有空引用,在程序中必须确保引用是和一块正确的存储区域关联;

*3.引用通常用于函数的参数表中或作为函数的返回值,前者因为使用引用作为函数参数不产生临时对象,可提高
程序执行效率和安全性,后者则因为引用作为函数返回值可以用于赋值运算符的左边;(实现变量地址交互)

*4.引用的作用与指针有些类似,它会对内存地址上存在的变量进行修改,但它不占用新的地址,从而节省空间,二者
除使用形式不同,本质也不同:指针是低级的直接操作内存地址的机制,可由整型数强制类型转换得到,功能强大
但极易出错:引用则是较高级的封装了指针的特性,不直接操作内存地址,不可由强制类型转换而得,安全性较高;

*5.虽然不能直接定义对数组的引用,但可以通过typedef来间接的建立对数组的引用,如下
......
typedef int array[10]; //定义int型数组类型array
......
array a={12,23,34,......};//定义array型数组a
array & b=a; //定义数组a的引用b;

*6.引用和指针的关系
引用和指针都会对内存地址上存在的变量进行修改,但引用不占新的地址空间,从而节省开销.
指针直接操作内存地址,可由整型数强制类型转换得到,功能强大但易出错;
引用不直接操作内存地址,不可由强制转换而得到,安全性较高


13.泛型算法应用于普通数组
1>数组的声明:
*1.一维数组: 数据类型 数组名 [常量表达式]; int a[6]; a为内存首地址,是地址常量;
*2.二维数组: 数据类型 数组名[行数][列数]; int a[2][3];行数可省略,列数不能省略;

e.g 对整型二维数组a[3][2]赋值
*1.在定义的同时赋值
int a[3][2]={0}; // 所有数组元素均为0;
*2.常规的赋值方法
int a[3][2]={1,2,3,4,5,6};
*3.分行的赋值方法
int a[3][2]={{1,2},{3,4},{5,6}};
*4.部分赋值方法
int a[3][2]={{1,2},{0},{3}};每行确少的数使用默认值0;

 

2>对数组的操作说明:
*1.数组不能作为整体输出,C++引用STL库提供的泛型算法,大大简化数组操作(操作与元素类型无关);
*2.对数组内容进行升幂,输出,反转和复制等操作需要包含头文件<algorithm>;
*3.对数组内容进行降幂和检索等操作需要包含头文件<functional>.
*4.假设一维数组a和b的长度为Len,数据类型为Type,则对数组内容的相关操作和语句如下:
1)数据内容的反转:
reverse(a,a+Len); //数组元素反转排列

2)复制数组内容
copy(a,a+Len,b); //将数组a的内容原样复制到数组b
reverse_copy(a,a+Len,b); //逆向复制数组a中内容到数组b

3)数组升幂排序
sort(a,a+Len); //默认排序方式是升幂

4)数组降幂排序:
sort(b,b+Len,greater<Type>()); //数组降幂排序

5)检索查找数组内容:
find(a,a+Len,value); //查找数组a中是否存在值为value的元素,find函数返回的是位置指针,
一般使用判别语句输出查找的内容,如
Type *x=find(a,a+Len,value); //x是类型type的指针
if(x==a+Len) cout<<"没有值为value的数组元素";
else cout<<"有值为value的数组元素";

6)若二维数组y有m列,则位于y[i][j]之前的元素个数为 i*m+j;


14.程序的编辑,编译和运行的基本概念
1> C++程序编制过程
*1.使用编辑器编辑一个C++程序mycpp.cpp,又称其为C++的源程序;
*2.使用C++编译器对C++程序进行编译,产生mycpp.obj;
*3.在使用连接程序(又称Link与库函数连接),将mycpp.obj变成mycpp.exe文件;
预处理-->编译(.cpp->obj)-->连接-->运行

*4.C++程序的编译是以文件为单位进行的;

2>创建C++项目
打开vc6.0->File->New->Project->Win32 Console Application->project name->ok->finish
->File->Flies->C++ source File->File(1210);
调试字体格式: Tools->Format->Font size;
调试Debug ctrl+F10, 小手是打断点;

e.g

#include <iostream>
using namespace std;

int max(int x,int y){
return x>y? x:y;
}

void main(){
void getStr();//如果方法写在main()下面要先声明;
int a,b;
a=5;
b=8;
cout<<"maxValue is "<<max(a,b)<<endl;
getchar();

//getStr();//后调用
}

void getStr(){
cout<<"hello,C++"<<endl;
cout<<"you"<<endl;
getchar();
}

第二章 从结构到类的演变
1.函数和数据共存
C++中首先允许结构中可以定义函数,这些函数称为成员函数,形式如下:
struct 结构名{
数据成员
成员函数
};
可以像C语言中结构变量使用结构成员的方法那样,通过C++的结构对象使用成员函数.
形式:结构对象.成员函数;

C++相对于C语言引入了类和对象的概念这是本质区别;

2.封装性
如果在定义结构时,将数据成员使用private关键字定义,则产生封装性,没有使用private定义的成员变量,默认为public;
私有的数据成员,不能直接使用,必须通过公有的成员函数才能使用,否则就会报错,这称为数据的封装性;

封装,就是把对象的属性和操作结合成一个独立的系统单位,并尽可能隐蔽对象的内部细节,对外提供接口(成员函数);
存取权限 public 和private

3.使用构造函数初始化结构的对象
函数名与结构同名,称为构造函数,专门用于初始化结构的对象;
构造函数的形式 构造函数名 对象名(初始化参数);
*.程序在运行时,会自动完成初始化任务;

4.从结构体演化为简单的类
1>用关键字class代替struct,就是一个标准的类.
2>类:类名,属性(或称数据成员) 成员函数;
3>因为struct是C语言中的,再用不安全,就用class 声明
4>对象:现实世界中客观存在的事物;
5>结构化程序设计使用功能抽象,面向对象程序设计不仅能进行功能抽象,还进行数据抽象,
对象实际上是功能抽象和数据抽象的统一;
6>类中的成员变量默认为private;
7>在类中声明任何成员不能使用extern,auto和register关键字修饰,类中声明的变量属于该类;

5.继承
继承是一个类可以获得另一个类的特性的机制,支持层次概念,通过继承,低层次的类只需定义特定于
它的特征,而共享高层的类中的特征;(母模与子模);(传递性)

6.多态性
不同的对象可以调用相同名称的函数,但可导致完全不同的行为的现象称为多态性;
*.函数重载的目的是提高代码的可读性;

实例

#include<iostream>
using namespace std;

class Point{//定义Point类
private:
double x,y;//类Point的数据成员

public:
Point(){}//类Point的无参构造函数
Point(double a,double b){x=a;y=b;}//具有两个参数的构造参数*/
void setxy(double a,double b){x=a;y=b;} //成员函数,用于重置数据成员
void print(){cout<<x<<'\t'<<y<<endl;} //成员函数,按指定格式输出数据成员

};


void main(){

Point point1;//声明point对象 point1

Point point2(10.0,20.0);//声明point对象并初始化其成员变量 point2

point1.setxy(4.5,8.0);//显示对象point1的数据成员赋值;

point1.print();//显示对象a的数据成员
point2.print();//显示对象b的数据成员

getchar();//或者cin.get();以停留结果窗口;

}

7.使用string对象
1>在程序中可以使用string类定义存储字符串的对象,这些对象属于string类,因此还要使用
#include<string>来包含这个列的头文件.

2> string对象的声明和连接
string str="AB"; str[0]='a';str[1]='b'; 字符串的连接 str1=str1+""+str1;

3>string类常用的成员函数
substr: string newStr=str1.substr(3,3); //截取第3位到第5位的字符,从0位开始数;
find: int i=str1.find("are",0); //查找子串的字符下标位置
getline: getline(cin,InputLine,'\n'); //截取一行
swap: str1.swap(str2);=str2.swap(str1);字符串内容互换;
begin和end:copy(str1.begin(),str1.end(),str2.begin);

8.使用complex对象
1> C++标准库提供complex类定义复数对象,在程序中包含这个类的头文件为:#include<complex>
复数(complex number)类需要两个初始值:实部和虚部,complex是一个模板类,格式如下:
complex<数据类型> 对象名(实部值,虚部值);
complex<int>num1(2,3);//定义复数2+3i
complex<float>num1(2.5,3.6);//定义复数2.5+3.6i

2> 实部和和虚部值的输出方法分别为real();和imag();
cout<<num2.real()<<" ,"<<num2.imag()<<endl;

第三章 函数及函数模板

1.函数的参数
概念:形参和实参,主调函数把实参的值传给被调函数的形参,形参是实参的备份;

1>形参如果为指针(int *p)(传地址,传数组名相当于传地址)或别名(传引用)(int &a)这两种类型,
则实参对象和形参对象为同一对象,所以改变形参对象的值就是改变实参对象的值,一变都变;

2>默认参数:int max(int a,int b=0);其中b有默认参数,传递实参的时候可以 max(1);
*传递实参时,默认参数后面不能再有实参值

3>传值分为传变量值和传变量地址值两种;
*实参带&传的是地址,形参带&传的是别名;

2. 函数的返回值类型
1> C++函数的返回值类型是除 数组 和 函数 以外的任何类型,非void类函数必须向调用者
返回一个值,数组只能返回地址;

2> 当返回值是指针或引用对象时,要特别注意:函数返回所指的对象必须继续存在,
因此不能将函数内部对象作为函数的返回值;

3> 返回引用的函数,函数可以返回一个引用,使该函数在赋值运算符的左边;声明方式如下:
[声明格式]
数据类型 & 函数名 (参数列表) ->引用函数
[e.g]
int a[]={1,2,3,5};//定义全局数组a
int & index(int i);//返回引用的函数index的原型声明

void main(){
index(3)=16;//将a[3]的值改为16
cout<<index(3)<<endl;//输出a[3]的值
}

int & index(int i){
return a[i]; //定义返回引用的函数index,实际上返回的就是a[i]
}

4> 返回指针的函数->指针函数:返回值是存储某种类型数据的内存地址的函数;
[声明格式]
数据类型 *函数名 (参数列表);
[e.g]
float *input(int& n){
cout<<"input number";//询问数据数据的个数
cin>>n; //输入个数
if(n<=0)return NULL;//输入的个数不合理退出
float *buf=new float[n];//根据输入数据的个数申请所需空间
if(buf==0) return NULL;//申请不到空间退出
for(int i=0;i<n;i++){
cin>>buf[i]
}
return buf; 返回指针
}

5> 函数的返回值可以当成其他函数的实参使用;

3.内联函数
1> 使用关键字 inline,内联函数必须在程序第一次调用此函数的语句出现之前定义;

2> C++中,除具有 循环语句 , switch语句 的函数不能使用内联函数外,其他函数都可以使用内联函数;

3>使用内联函数可以提高程序的可执行速度,相当于宏展开,但如果函数体语句多,则会增加程序代码量;

4>示例
#include<iostream>
using namespace std;
inline int isNumber(char c){
return (c>='0'&&c<='9')?1:0;
}

if(isNumber(c)){
cout<<"你输入了一个数字";
}else{
cout<<"你输入的不是一个数字";
}

4.函数重载

1> 目的:使用方便,提高代码可读性。

2> 函数名相同 :凭借参数类型不同,参数的个数不同,注意:单凭返回值类型不同不能区分重载函数;

3> 函数重载时,源代码只指明函数调用,而不说明具体调用那个函数,编译器这种连接方式
称为动态联编或迟后联编;

4> 当函数重载与默认参数相结合时,能有效减少函数个数及形态,缩减代码规模;

5> 如果使用默认参数,就不能对参数个数少于默认参数个数的函数形态进行重载,只能对多于默认
参数个数的函数形态进行重载;

6> 在函数调用时,如某一默认参数要指明一个特定值,则有其之前所有参数(实参)都必须赋值

7> 默认参数是函数在函数原型中说明的,默认参数可以多于1个,但必须放在参数序列的后部。

5.函数模板
1> 概念:在程序设计时没有使用实际的类型而是使用虚拟的类型的参数,当用实际类型实例化这种函数
时就好像按照模板来制造新的函数一样,故称为函数模板;

2> C++中规定模板以关键字template和一个形参表开头,形参表中以class或typename表示"用户定义的或固有的类型"
一般用T作为标识符来标识类型参数;

3> 注意事项:
*1.定义函数模板时,函数模板不限包含语句"template<tempname T>"不能因为前面声明过了就不写,一个函数template
语句只管一个函数模板,有几个函数模板,就要声明几个"template<tempname T>"语句;

*2.如果函数模板的参数列表中的参数类型不同,或参数类型相同,但需要转化参数类型时,一定要显示的给出比较标准,如
"min<double>(8.5,6)"或对参数表中的参数进行强制转换如"max((int)8.5,6)",以便使程序正确的推出模板参数;

[e.g]实例如下

#include<iostream>
using namespace std;
template<typename T>
T max(T m1,T m2){
return (m1>m2)?m1:m2;
}

template<typename T> //必须重写
T min(T m1,T m2){
return (m1<m2)?m1:m2;
}


void main(){
min<double>(8.5,6); min(8.5,(double)6);
cout<<max(2,5)<<"\t"<<min(2,5)<<endl;
cin.get();
}


第四章 类和对象
1.类及其实例化
1>声明类:
对象就是一类物体的实例,将一组对象的共同特征抽象出来,就形成了类的概念;
[类的声明形式]
class 类名{

private:
私有数据和函数 (默认成员的修饰符为private(不写也是private))

public:
公有数据和函数

protected
保护数据和函数 (只有其派生类可以使用该数据或函数)

};

2>类的定义
类是对一组性质相同的对象的程序描述,也属于一种自己构造的数据类型,要先声明后使用,具有唯一标识符
的实体,在类中声明的任何成员不能使用extern,auto和register关键字修饰,类中声明的变量属于该类等

[注意事项]
与其他数据类型不同的是,类除了数据,还可以有对数据进行操作的函数,分别称为类的数据成员和成员函数,
而且不能在类的声明中对数据成员使用表达式进行初始化;定义类时系统不给类开辟空间,所以类中成员不能
直接初始化;只有在外部初始化化类后得到对象,其对象可给本类的成员变量初始化

2.定义成员函数
1>成员函数在声明后还必须在程序中通过定义来实现对数据成员的操作.
[定义成员函数的一般形式]
返回类型 类名::成员函数名(形参列表){
成员函数体 //内部实现
}

2>其值"::"是作用域运算符,类名是指成员函数所属类的名字,用于表示其后成员函数属于这个特定的类.
其中返回类型是指这个成员函数返回值的类型;

3>在类体外写类的成员方法形式:
*1.在类体内声明: inline(定义内联) 返回值类型 类名::函数名(形参列表);
*2.在类体外定义:
inline(定义内联) 返回值类型 类名::函数名(形参列表){
成员函数体//内部实现
}
[e.g]
#include <iostream>
using namespace std;
class Point{ //使用内联函数定义类point
private:
int x,y; //私有数据成员 不能在类体内部初始化;

public:
Point();
Point(a,b){x=a,y=b;}//这是*1
Point(int,int);//这是*2的声明
void setXy(int a,int b){x=a;y=b;}//这是*1.在里面写的,默认为inline函数 下面的*2:是在类外面写的,
inline void setXy(int a,int b);//这是*2.这个要写在类型外,要在类体内声明;而且在外边定义不默认为内联函数
void move(int a,int b) {x=x+a;y=y+b;}//类内部的函数为内联函数
void display(){cout<<x<<","<<y<<endl;}
int getX(){ return x;}
int getY() { return y;}
~Point();
}; //类定义以分号结束
inline void Point::setXy(int a,int b){x=a;y=b;}//这是*2,*2不能和*1同时出现 使用inline定义内联;
Point::Point(int a,int b):x(a),y(b){cout<<"init"<<a<<" ,"<< b << endl;}
void print(Point *a){a->display();}
void print(Point &a){a.display();}
void main(){
Point A,B,*p;//声明对象和指针
Point &RA=A; //声明对象RA为对象A的引用
A.setXy(25,55); //使用成员函数为对象A赋值
B=A;
p=&B;
p->setXy(112,115); 使用指针调用函数setXy重设B的值
p->display(); //使用指针调用display函数显示对象B属性 指针才能用->
printf(p); //传递指针显示对象B的属性
RA.move(-80,23);
print(A); //使用对象和对象指针效果一样;
print(RA);
}

3.数据成员的赋值和初始化
1>不能在类体内或类体外给数据成员赋值,要调用类的赋值成员函数或构造函数赋值;

2>初始化是指产生对象时就使对象的数据成员具有指定值,它是通过使用与类名相同的构造函数实现的;
[e.g]Point B(12,25);//给Point型对象B赋值;

3>构造函数的功能
用来实现对象初始化的特殊成员函数:
系统默认一个无参构造函数;
如果已定义有参构造函数,则系统不会给默认无参构造函数,故存在需要先建立数组后
进行赋值操作的情况,必须为它定义一个无参构造函数
*1.给类对象开辟空间
*2.给类对象成员变量赋值;
*3.对象的构造函数调用顺序取决于对象成员在类中声明的顺序;
*4.指针中用到new开辟空间就要delete释放空间 如 Point *ptr1=new Point; delete ptr1;
*5.使用new创建的对象,其生命周期由用户控制;

4>析构函数
功能:在对象的生存期结束时被自动调用,然后对象占用的内存被回收;(顺序:堆栈:后进先出;)
~Point(); ~类名();放在所在类的最后"}"之前,没写的话,系统会默认一个析构函数;
类外声明:类名::~类名(){} //不允许重载
类的对象数组的每个元素调用一次析构函数,全局对象数组析构函数在程序结束之前被调用;

运算符delete删除一个动态对象
运算符delete与析构函数一起工作。它首先为这个动态对象调用析构函数,然后再释放这个动态对象占用的内存,
当delete释放动态对象数组时,即delete[ ]ptr; 不适用于空指针;
*注意:有使用new 开辟空间的先调用该类的析构函数在调用delete,释放new开辟出的空间;

5>复制构造函数
功能:复制构造函数,就是通过拷贝方式使用一个类已有的对象来建立该类的一个新对象,
所以又直译为拷贝构造函数,通常编译器会建立一个默认复制构造函数,主要提高效率和安全性
[格式]
类名::类名(const 类名& 对象名[声明时可不写,定义时必须写]),
*.可以不加const修饰,但必须使用 对象的引用 作为参数;传引用不开辟空间,不调用构造函数
如:Point::Point(Point& t){x=t.x;y=t.y;}//一旦自定义,系统就不再默认;
A::A(const A&);

调用复制构造函数场景
*.不想再开辟空间和释放空间;
例如:Point A(15,22); Point B(A)==>Point B=A; Point(int &p)

 

4.使用类对象
1>类的成员函数可以直接使用自己类的私有成员(数据成员和成员函数);
2>类外面的函数不能直接访问类的私有成员,而只能通过类的对象使用该类的公有成员函数;
3>对象A和B的成员函数的代码一样,两个对象的区别是属性的取值;
4>除指针对象都用"."调用类中的成员函数,语法如下:
对象名.对象成员名
如果是指针语法如下:
类名 *对象指针名=对象的地址;
通过"->"运算符访问对象的成员,即:
对象指针名->对象成员名

5.this指针
1>概念:
当一个成员函数被调用时,系统将自动向它传递一个隐含的参数,该参数是一个指向调用该函数的
对象的的指针,名为this指针,从而使成员函数知道该对哪个对象进行操作;
2>作用:
this指针,保证了每个对象拥有自己的数据成员,并共享处理这些数据成员的代码;

6.一个类的对象可以作为另一个类的成员实例;


6.类的性质
1>使用类的权限
*1.类本身的成员函数可以使用类所有成员(私有和公有成员).
*2.类的对象只能访问公有成员函数,例如输出x只能使用A.getX(),不能使用A.x;
*3.其他函数不能使用类的私有成员,也不能使用公有成员函数,它们只能通过定义类的对象
为自己的数据成员,然后通过类的对象使用类的公有成员函数.
*4.虽然一个类可以包括另外一个类的对象,但这个类也只能通过被包含的类的对象使用那个类
的成员函数,通过成员函数使用数据成员,如Loc.set(x,y);
Point *p=rect.getLoc();-->int x=p->getX(),int y=p->getY();

2>不完全类的声明
class MemberOnly;//不完全类声明;没有方法体
不完全声明的类不能实例化,否则会编译出错,不完全声明仅用于类和结构,且不能存取不完全类成员(除指针)

3>空类 class Empty{public:Empty(){}};

4>类的作用域
*1.声明类时所使用的一对花括号形成类的作用域,在类作用域中声明的标识符只在类中可见,
如class classOuter{int num;};也就是在声明类的时候可以定义类中的成员数据;

*2.如果某成员函数的实现是在类定义之外给出的,则类作用域也包括该成员函数的作用域,因此
当在该成员函数内使用一个标识符时,编译器会先再类定义域中寻找;
[e.g]
class MyClass{
public:
int number;//定义属于类MyClass的整型数number
void set(int);
};

int number;//这个number不属于类MyClass
void MyClass::set(int i){
number=i;//使用的是类MyClass中的标识符number;
}

7.面向对象的标记图
OOP:面向对象的实现-->OOA:面向对象的分析-->OOD:面向对象的设计
UML(统一建模语言):可视化的建模语言,主要用于面向对象的分析和建模;

8.对象的结构与连接
对象的结构是指对象之间的分类(继承)关系和组成(聚合)关系,统称关联关系;
对象的连接是指实例连接和消息连接:对象行为之间的动态关系是通过对象行为(消息)
之间的依赖关系表现的,称为消息连接

9.对象定义的组成要素:
*1.对象的属性:指描述对象的数据成员,对象属性的集合又称为对象的状态;(静态特征)
*2.对象的行为:定义在对象属性上的一组操作的集合(动态特征)

10.编译指令
C++每行只能写一条编译指令
#include "file" 自定义的用"",系统的用<>;
#define #undefined
条件编译指令:#if #else #endif #ifdef

第五章 特殊函数和成员

注意:简单函数是指声明中不含:const,volatile,static关键字
1.对象成员的初始化
1> 概念:可以在一个类中说明具有某个类的类型的数据成员,这些成员称为对象成员;
int x;//数据成员 getX()//成员函数 类名 成员名;//对象成员
[对象成员初始化格式] A::A(参数表0):成员1(参数表1),成员2(参数表2),...
*注意:构造函数不能被对象显示调用;

2> 对象成员构造函数的调用顺序取决于这些对象成员在类A中说明的顺序,与它们在成员初始化
列表中的顺序无关;当建立A类对象时,先调用对象成员的构造函数,初始化对象成员,然后才
执行A类的构造函数,初始化A类中的其他成员,析构函数与构造函数的调用顺序相反;

class object{
private :
int val;
public:
object(int i):val(i){cout<<"init"<<endl;}
~object(){cout<<"destroy"<<endl;}
}

class container{
private:
object one;
object two;
int data;
public:
container(int i,int j,int k);
~container();
}
container::container(int i,int j,int k):two(i),one(j)

3>特殊成员的初始化
const int num 修饰的和 &修饰的(int& ret) 只能用参数列表初始化

2.静态成员函数
1>初始化:被static修饰的为静态成员,静态成员的初始化 int Test::x=25;
2>静态成员函数与一般成员函数的区别:
*1.可以不指向某个具体的对象,只与类名连用;
*2.在没有建立对象之前,静态成员就已存在;
*3.静态成员是类的成员,不是对象的成员;
*4.静态成员为该类的所有对象共享,它们被存储于一个公用内存中;
*5.没有this指针,只能通过对象名或指向对象的指针访问类的数据成员;
*6.静态成员函数不能被声明为虚函数;
*7.静态成员函数不能直接访问非静态函数;
*8.静态数据成员可以直接用类名或者对象名来调用

#include <iostream>
using namespace std;
class Test{
public:
static int y ;//静态成员数据
};
int Test::y=0;//初始化静态成员
int main(){
Test test;
test.y=1;
Test test1;
test1.y=6;
//静态数据成员可以直接用类名或者对象名来调用
cout << " test.y="<<test.y<<endl<<" ,Test::y="<<Test::y<<endl;
return 0;
}

//特殊成员
class Test{
int x ;//静态成员数据
const int num1; // 常量成员
static const int num2; // 常量成员
int& ret; // 引用成员
public
static int y ;//静态成员数据
Test(int n,int f,int a):num(n),ret(f){} //初始化列表形式初始化对象成员或引用和常量;
Test(int,int);
static int func(){return x;}
};
int Test::y=25; //初始化静态数据成员,静态成员仅仅初始化这一次
const int Test::num2=125;//静态常数据在类体外初始化
Test::Test(int i,int j):x(i),num1(j),ret(x);//初始化参数列表用作给const修饰的非静态数据的init
void main(){
cout<<Test::func()<<endl;
}

3.友元函数
1>在类中用friend关键字修饰的声明函数,但定义在类的外部,被称为友元函数,
2>功能:有权访问类中所有私有和保护成员 提高了效率 ,但破坏了封装性;
3>注意:友元函数不是类的函数,不带this指针;
4>友元声明与访问控制无关。友元声明在私有区域或在公有区域进行是没有太大区别的。
友元函数的声明可以置于任何部分。
*示例:
class Point{
private:
double x,y;

public:
Point(double xi,double yi){x=xi,y=yi;}//构造
double getX(){return x;}
double getY(){return y;}
friend double distances(Point&,Point&);//声明友元函数,但不是Point的成员函数
friend void One::func(Two&);//声明类One的成员函数为本类的友元函数;
};

void One::func(Two& r){
r.y=x;//类One的成员函数为Point的友元函数;可直接访问其私有数据;
}
//定义两点之间距离 √(x2-x1)^2+(y2-y1)^2;
double distances(Point&a,Point&b){//定义友元函数
double dx=a.x-b.x;//因为是友元函数所以可以直接访问对象的私有成员;
double dy=a.y-b.y;//因为是友元函数所以可以直接访问对象的私有成员;
return sqrt(dx*dx+dy*dy);//√(x2-x1)^2+(y2-y1)^2; 两点间的距离(直角斜边长度)
}
void main(){
Point p1(3.5,4.0),p2(4.5,6.5);
cout<<distances(p1,p2)<<endl;
}
5>类的友元
#include<iostream>
#include <cmath>
using namespace std;
class Two{
private:
int y;
public:
friend class One;//声明类One是Two的友元
};

class One{
private:
int x;
public:
//one是Two的友元可以直接访问Two的私有变量r.y
One(int a,Two&r,int b){x=a;r.y=b;}
void func(Two&)
};

4.常对象及常成员函数
1>声明常对象
类名 const 对象名(参数表);//必须在声明的同时进行初始化
例如:定义Base类的常对象:Base const a(24,35);

2>常成员函数
*1.作用:为了防止覆盖函数改变数据成员的值,可以将一个成员函数声明为const函数,
const函数不能更新对象的数据成员,也不能调用该类中没有用const修饰的成员函数;

*2.用const声明static成员函数没作用,另外,在C++中声明构造函数和析构函数时,
使用const关键字是非法的,而volatile关键字的使用方法与const类似;

*3.声明const成员函数的格式:类型标识符 函数名(参数列表) const
对于const对象只能调用const函数,不能调用非const函数

void show()const;//声明常量函数
void Base::show()const{cout<<x<<endl;}//常函数的定义 const在函数名后
Base const b(10);//初始化常对象 const在类名后,对象名前 virtual(虚)相同
b.show()//b作为常对象只能调用常函数,否则会报错;

[例如]
#include<iostream>
using namespace std;
class Base{
private:
const double p;//声明常成员函数
public:
Base(double m):p(m){} //声明Base构造函数 p为常成员数据只能用初始化列表初始化
void show() const;//声明常函数
};
void Base::show()const{cout<<p<<endl;}
void main(){
Base const b(10);//const在对象名前,类名后
b.show();//常对象只能调用常函数;
}

5.对象数组
Test one[2]={Test(1),Test(2)};
定义类的动态对象数组时,系统只能自动调用该类的无参构造函数对其进行初始化;
6.指向类的成员函数的指针
#include <iostream>
using namespace std;
class A{
private:
int val;
public:
A(int i){val=i}
int value(int a){return val+a;}
};

void main(){
int(A::*pfun)(int);//声明指向类A且有一个形参为int型的成员函数的指针pfun
pfun=A::value;//将类A中符合指针pfun要求的value函数的地址赋值给指针pfun
A obj(10);
cout<<(obj.*pfun)(15)<<endl;//obj.*pfun相当于obj.value(15)其值为15+10=25
A *pc=&obj;//将obj的地址赋值给指针pc
cout<<(pc->*pfun)(15)<<endl;//指针pc调用也相当于obj.value(15)其值为15+10=25
}
(obj.*pfun)或(pc->*pfun)必须用括号括起来,类似于指向对象的指针 *要写到函数名前

*指向类数据成员的指针要求数据成员是公有的;

7.求解一元二次方程
ax^2+bx+c=0;
解法:
1>求证判别式:b^2-4ac的值和0作比较
*1. >0 //在实数范围内有两个不相等的实根
*2. =0 //在实数范围内有两个相等的实根
*3. <0 //在实数范围内没有实根
2> x=(-b+-√(b^2-4ac))/2a //根号sqrt
例如:x^2-3x+2=0;求x的值
a=1,b=-3,c=2;
先判断根数:
-b=3
b^2-4ac=9-8=1;//值大于0所以有两个不相等的实根
(-b+√(b^2-4ac))/2a=(3+1)/2=2;
(-b-√(b^2-4ac))/2a=(3-1)/2=1;
所以x=1,2;

第六章 类的继承和派生

1.继承和派生的概念:
1>类的派生是指从一个或多个以前定义的类产生新类的过程,原有的类称为基类,
新产生的类称为派生类,派生类继承了基类所以的数据成员和成员函数;
C++中有两种继承:单一继承和多重继承;

2>类的继承是指派生类继承基类的所有数据成员和成员函数成为自己的成员,继承常用来表示类属关系,
不能讲继承理解为构成关系.( is a的关系)
*1.隶属关系是指在类目表中下位类一定要带有上位类的属性,上位类一定能包含它所属的各级下位类。
*2.C++中的两种继承方式:
1)单一继承:派生类只有一个基类
2)多重继承:派生类可以有多个基类
*3.静态成员可以被继承,这时基类对象和派生类的对象共享该静态成员;
*4.构造方法和析构函数不能继承,所有派生类都用自己的构造方法,
友元函数不是类的成员函数所以派生类也不能继承父类的友元函数;

[单一继承格式]
class 派生类名:访问控制 基类名{
private:
成员声明列表
public:
成员声明列表
protected;
成员声明列表

};
* 访问控制是指如何控制基类成员在派生类中的访问属性,它的关键字为private,public,protected中的一个
一般情况下为public(可以访问基类中的公有成员函数);其他声名和一般类一样;

#include<iostream>
using namespace std;
class Point{
private:
int x,y;
public:
Point(int a,int b){x=a;y=b;}
void showXy(){cout<<"x==>"<<x<<"y=>"<<y<<endl;}
}
class Rectangle:public Point{
private:
int H,W
public:
Rectangle(int a,int b,int h,int w):Point(a,b){H=h;W=w;}
void show(){cout<<"H==>"<<H<<"W=>"<<W<<endl;}
}


3>派生类的特点
*1.增加新成员(数据成员或成员函数)
*2.重新定义已有的成员函数.
*3.改变基类成员的访问权限.

2.访问权限和赋值兼容规则
公有派生和赋值兼容规则
在公有派生情况下,基类成员的访问权限在派生类中保持不变,
*1.基类的公有成员在派生类中仍然是公有的 public
*2.基类的保护成员在派生类中仍然是保护的(保护的能让自己的基类对象访问,不能被外界其他函数访问) protected
*3.基类的不可访问的和私有的成员在派生类中也仍然是不可访问的(私有派生类不能访问) private

//公有继承
基类成员 派生类成员函数(类内) 基类和派生类对象 外部函数
private成员 不可访问 不可访问 不可访问
protected成员 protected(访问权限不变) 不可访问 不可访问
public成员 public(访问权限不变) 可访问 可访问

//私有继承或保护继承
就是在派生类中,基类是私有的,派生类不可访问,基类是其他访问权限的成员
权限全部变为相应的私有成员(私有继承)或保护成员(保护继承);

//访问属性
private 只允许该类的成员函数及友元函数访问,不能被其他函数访问;
protected 既允许该类的成员函数及友元函数访问,也允许其派生类的成员函数访问
public 既允许该类的成员函数,也允许类外部的其他函数通过类的对象访问

3.派生类的构造函数和析构函数
1>派生类构造函数格式
派生类名::(参数表0):基类名(参数表){...//函数体}
*1.冒号后"基类名(参数表)"称为成员初始化列表,参数表给出所调用基类构造函数所需要的实参,实参值可来自
参数表0,也可由表达式给出.例如public Rectangle(int a,int b,int h,int w):Point(a,b){H=h;W=w;}

2>派生类析构函数格式
派生类名::~派生类名(){...//函数体}
在派生类内 ~派生类名(){...//函数体} //内联函数
*.构造函数和析构函数也都可以在类体内直接定义为内联函数,这时定义新式需要把上述定义式中的"派生类::"去掉.
[e.g]
#include <iostream>
using namespace std;
class Point{
protected:
int x,y;

public:
Point(int a,int b){x=a;y=b;}
void show(){cout<<"x="<<x<<" ,y="<<y<<endl;};
};

class Rectangle : public Point{
private:
int H,W;

public:
Rectangle(int,int,int,int);
//由于Point中的x,y是protected的故派生类Rectangle可以直接调用输出;
void show(){cout<<"x="<<x<<" ,y="<<y<<" ,H="<<H<<" ,W="<<W<<endl;};
};
Rectangle::Rectangle(int a,int b,int h,int w):Point(a,b){H=h;W=w;}
void main(){
Point p1(5,6);
Rectangle r1(1,2,3,4);
p1.show();//result->x=5 ,y=6
r1.show();//result->x=1 ,y=2 ,H=3 ,W=4
}

4.公有继承"isa"和分层"has-a"的区别
1>isa:"就是一个的含义"->如果类B公有继承与类A,则在使用类A的对象的任何地方,类B的对象同样也能使用,
即每个类B就是一个类A的对象,但反之则不行,-->类B可转化为类A 而类A不能转化为类B;

2>has-a:"有一个的含义"->也就是一个类中包含另一个类的对象作为本类的数据成员;如person类中包含name和age

5.多重继承
class 类名1:访问控制 类名2,访问控制 类名3,...,访问控制 类名n{//类体}

6.二义性及其支配规则
1>二义性:如果一个表达式的含义能解释为可以访问多个基类中的成员,则这种对基类成员的访问就是不确定的,
称这种访问具有二义性,C++规定对基类成员的访问必须是无二义性的;

*1.二义性检查是在访问权限之前进行的,因此成员的访问权限不能解决二义性问题.
*2.如果涉及几层继承关系,对于任意基类中可以存取的成员,都可以通过作用域分辨进行存取;

2>通过作用域分辨符和成员名限定消除二义性;
[格式] 类名::标识符(该类下任意成员成员)
[e.g]
#include<iostream>
using namespace std;
class A{
public:
void func(){cout<<"func"<<endl}
}

class B{
public:
void func(){cout<<"func"<<endl}
}

class C:public A,public B{
public:
void hun1(){A::func();}
void hun2(){B::func();}
}

void main(){
C obj;
//如果类C中没有func()函数,则不能直接写obj.func();
obj.A::func();
obj.B::func();
}

3>支配性
*1.派生类顶替基类中的同名函数;
*2.如果想使用基类的同名函数使用作用域分辨符进行存取 类名::标识符(该类下任意成员成员)
*3.通过作用域运算符存取与局部变量同名的全局变量;

4>友元函数与访问控制无关,且不能被其派生类继承,派生类的友元只能访问该派生类的直接基类
的公有和保护成员,不能访问基类的私有成员;

第七章 类模板和向量

1.类模板
1>概念:如果将类看做是包含某些数据类型的框架,然后将这些数据类型从类中分离出来形成一个通用
的数据类型T,为其设计一个操作集,并允许原来那些数据类型的类都能使用这个操作集,这就能避免
因为类的数据类型不同而产生的重复性设计,这种对类的描述的类型T称为类模板;
*.类模板实例化时,编译器将根据给出的模板实参生成一个类;

*在编译时,由编译器将类模板与某种特定数据类型联系起来,就产生一个特定的类,称为模板类,
由此可见,利用类模板能大大简化程序设计;

[e.g]

template<class T>//带参数T的类模板声明,可用typename替代class
class TAnyTemp{
private:
T x,y;//声明类型为T的私有数据成员

public:
TAnyTemp(T a,T b)x(a),y(b){}//类TAnyTemp的构造函数,实参类型为T
T getX(){return x;}//返回类型为T的内联成员函数
T getY(){return y;};//返回类型为T的内联成员函数
}

2>类模板的成分及语法
[形式] template <类型模板参数> class 类名{ //类体};
类模板参数中的class意为"任意内部类型或用户定义类型",T数结构或类;
template<class T>只能作用于一个{//类体},到第二个类要重新声明


3>类模板对象
类模板也称参数化类,初始化类模板时,只要传给他指定的数据类型,编译器就用会指定
类型替代模板参数产生相应的模板类;

[类模板定义对象的一般格式]
类名<模板实例化参数类型>对象名;//默认或无参构造函数
类名<模板实例化参数类型>对象名(构造函数实参列表);//构造函数

[e.g]
#include<iostream>
using namespace std;
template <class T>//带参数T的类型模板声明,可用typename代替class
class TAnyTemp{ //类声明
T a,b,c,d;//四个类型为T的私有数据成员
//定义返回类型为T且参数类型为T的私有成员函数,返回a b两者中的最大值
T max(T a,T b){return (a>b)?a:b;}
public:
TAnyTemp(T,T,T,T);//类TAnyTemp的构造函数,含4个实参类型为T
T Max(); //声明返回值为T的公有成员函数
};
template <class T> //定义成员函数必须再次声明类模板
TAnyTemp<T>::TAnyTemp(T x1,T x2,T x3,T x4):a(x1),b(x2),c(x3),d(x4){} //构造
template <class T> //定义成员函数必须再次声明类模板
T TAnyTemp<T>::Max(){return max(max(a,b),max(c,d));} //取最大值
void main(){
TAnyTemp<char> a('W','w','a','A');//T与某种数据类型相关联
TAnyTemp<int> b(1,2,3,4);//模板类是类模板的实例
TAnyTemp<double> c(1.25,2.56,3.87,4.62);
cout<<a.Max()<<" ,"<<b.Max()<<" ,"<<c.Max()<<endl;
cin.get();
}


4>定义模板类的成员函数
在类体外定义成员函数时,必须用template重写类模板说明,[格式] template<模板参数>
返回类型 类名<模板类型参数>::成员函数名(函数参数列表){//函数体;}

*1.式中<模板类型参数>是指template后的"< >"内使用class(或tempname)
声明的类型参数,构造函数和析构函数没有返回类型.

*2.模板实例化参数类型包括数据类型和值,因为编译器不能从构造函数参数列表推断出
模板实例化参数类型,所以必须显示的给出对象的参数类型;
#include<iostream>
using namespace std;
template<class T,int size=4>//可以传递程序中的整数参数值
class Sum{
private:
T m[size];//数据成员

public:
Sum(T a,T b,T c,T d){m[0]=a;m[1]=b;m[2]=c;m[3]=d;}//构造函数
T s(){return m[0]+m[1]+m[2]+m[3];}//求和成员变量

};

void main(){
Sum<int,4>num(-1,23,58,2);//整数求和
cout<<num.s()<<endl;
cin.get();
}

*类模板中的数据类型不一定都相同,可以含有两个以上的T类型
如 template <class T1,class T2> Sum<int,double> num;

 


*.区分类模板与模板类的概念

 一个类模板(类生成类)允许用户为类定义个一种模式,使得类中的某些数据成员、默认成员函数的参数,
某些成员函数的返回值,能够取任意类型(包括系统预定义的和用户自定义的)。

类模板不是一个具体的类,使用时必须首先实例化为具体的模板类,然后通过模板类定义对象。
 
如果一个类中的数据成员的数据类型不能确定,或者是某个成员函数的参数或返回值的类型不能确定,
就必须将此类声明为模板,它的存在不是代表一个具体的、实际的类,而是代表一类类。

定义一个类模板 其格式为:
template<class T>
template<tempname T>
class test{....}

模板类:是类模板实例化后的一个产物;

5>类模板的派生和继承
类模板的基类和派生类都可以不是类模板,可以是普通类;
[e.g]
#include<iostream>
using namespace std;
template<class T>
class Point{
private:
T x,y;//数据成员

public:
Point(T a,T b){x=a;y=b;}//构造函数
void display(){cout<<x<<" ,"<<y<<endl;}//类模板Point的公有成员函数;
};

template<class T>
class Line:public Point<T>{
private:
T x2,y2;//数据成员
public:
Line(T a,T b,T c,T d):Point<T>(a,b){x2=c,y2=d;}//构造函数
void display(){Point<T>::display();cout<<x2<<" ,"<<y2<<endl;}//

};

void main(){
Point<double>a(1.23,58.2);
a.display();
Line<int>ab(4,5,6,7);
ab.display();
cin.get();
}

2.向量与泛型算法
1>概念:向量是一维数组的类版本,与数组类似,其中的元素项是连续存储的不同的是
*1.在数组生存期内,数组的大小是不变的,而向量中存储元素的多少可以在
运行中根据需要动态的增长或缩小;
*2.向量是类模板,具有成员函数,例如可以使用向量名.size()的方法动态获得
vector对象当前存储元素的数量;

2>向量的构造函数

向量(vector)类模板定义在头文件vector中,它提供4种构造函数,用来定义由各元素组成的
列表,如果用length表示长度,用type表示数据类型,用name表示对象名,则有以下4种构造:

*1.vector<type> name; //定义元素类型为type的向量空表
vector<char> A;//定义空char向量A 空表没有元素,它使用成员函数获取元素;

*2.vector<type> name(length); //定义具有length个类型为type的元素的向量,元素均初始化为0;
vector<int> B(20);//定义含有20个值均为0的int型元素向量B

*3.vector<type> name(length, a);//定义具有length个类型为type的元素的向量,元素均初始化为a;
/**
* @param type int 向量中元素的类型
* @param name C 向量对象的名称
* @param length 10 向量元素的个数
* @param a 1 向量元素的初始值
*/
vector<int> C(10,1)//定义含有10个值均为1的int型元素向量C
C.size()=10;//向量C中元素的个数;

*4.vector<type> Vector2(Vector1);//用已定义的向量Vector1构造向量Vector2
vector<int> D(C); //用向量C初始化D,使D与C一样;


3>利用函数size()循环输出向量B的内容
for(int i=0,i<B.size();i++){
cout<<B[i]<<endl;
}

4>利用数组初始化向量
不能使用列表初始化向量,但可通过定义一个数组,然后把数组的内容复制给向量的方法来初始化向量.
int IA[10]={0,1,2,3,4,5,6,7,8,9};
vector<int> VB(IA,IA+10);//IA代表数组的起始位置IA,到结束标志 IA+10
如果是IA+10就是将数组的结束标志位也给了向量;
/**
* 将数组转换成向量 (向量和数组的元素类型要一致)
* @param type 数组的元素的类型
* @param arrayName 数组的名称,是数组的首地址
* @param arrayName+length 数组首地址+数组长度
*/
vector<type> VectorName(arrayName,arrayName+length);

5>泛型指针
*1.概念:与操作对象的数据类型相互对立的算法称为泛型算法,泛型算法通过一对
泛型指针begin和end实现,元素存在范围时半开区间[begin,end.
a.begin():向量的开始位置,a.end():向量的结束标志
如果是rend():向量的开始位置和rbegin():向量的结束位置
double a[]={1,2,3,4}
vector<int> va(a,a+4);
copy(va.begin(),va.end(),ostream_iterator<int>(cout,""));

vector<char>::iterator p;表示向量的数据类型为char,指针名为p,
iterator相当于* iterator p==>*p;这是声明正向泛型指针的语法规范

#include<vector> //头文件

6> 向量基本的操作函数
*1.size();返回当前向量中已经存放的对象个数;
*2.max_size():返回向量最多可以容纳的对象个数,此参数取决于硬件结构,不由用户指定
*3.capacity():返回无需再次分配内存就能容纳的对象个数,其初始值为程序员申请的元素个数
*4.empty() :当前向量为空时,返回true值,内容不为空,返回0值
*前三者关系:max_size()>=capacity()>=size;

7> 访问向量中对象的方法
*1.front():返回向量中的第一个对象;
*2.back():返回向量中的最后一个对象;
*3.operator[](size_type,n);返回向量中下标为n(第n+1个)的元素;

8> 在向量中插入或删除对象的方法
*1.push_back(const T&); //向向量尾部插入一个对象.
*2.insert(iterator it,const T&); //向it所指的向量位置前插入一个对象.
*3.insert(iterator it,size_type n,const T&X);
向it所指向量位置前插入n个值为X的对象

9> 在向量中删除对象的方法
*1.pop_back(const T&);//删除向量中最后一个对象.
*2.erase(iterator it);//删除it所指向的容器对象;
*3.clear();删除向量中的所有对象,empty返回true值.

第八章 多态性和虚函数

1.多态性
1>静态联编所支持的多态性称为编译时的多态性,当调用重载函数时,编译器可以根据调用
时所使用的实参在编译时就确定下来应该调用那个函数;

2>动态联编 所支持的多态性称为运行时的多态性,由虚函数实现,虚函数类似于重载函数,
但与重载函数的实现策略不同,即对虚函数的调用使用动态联编,C++规定动态联编是在 虚函数
的支持下实现的。C++中要实现动态联编,调用虚函数时必须使用基类指针。

1)静态联编 所支持的多态性称为编译时的多态性,当调用 重载函数 时,编译器可以根据调用时
所使用的实参在编译时就确定下来应调用哪个函数,

2)动态联编所支持的多态性称为运行时的多态性,这由虚函数来支持。

*1.如果左面是基类的对象,右面是子类的对象 且其对象调用的方法,子类和父类都有
*2.调用的不是虚函数就调用父类的方法(左边静态联编),反之调用子类的方法(右边的(动态联编));
*3.不是虚函数看等号左边,是虚函数看等号右边;

*4.派生类能继承基类的虚函数表,而且只要是和基类同名(参数相同)的成员函数,
无论是否使用virtual声明,他们都自动成为虚函数;

*5.虚函数不能是静态成员(静态成员是在编译阶段定义的而虚函数是在运行阶段定义的)
例如 virtual void fun( );//声明虚函数

*6.程序产生运行时的多态性有3个前提:(*****)

①.类之间的继承关系满足赋值兼容性规则。

②.改写了同名虚函数。

③.根据赋值兼容性规则使用指针(或引用)。

*7.在调用中对虚函数使用成员名限定,可以强制C++对该函数的调用使用静态联编。
*8.当编译系统含有虚函数的类时,将为他建立一个虚函数表,表中的每一个元素都指向一个虚函数地址;
#include<iostream>
using namespace std;
const double PI=3.14159;
class Point{
private:
double x,y;
public:
Point(double i,double j){x=i,y=j;}
//double area(){return 0;}
virtual double area(){return 0;}
};

class Circle:public Point{

private:
double radius;
public:
Circle(double a,double b,double r):Point(a,b){radius=r;}
double area(){return PI*radius*radius;}

};


void main(){
Point a(1.5,6.7);
Circle c(1.5,6.7,2.5);

Point *p=&c;//派生类对象的地址赋值给指向基类的指针
cout<<"p->area()"<<p->area()<<endl;//调用基类的area()
Point &rc=c;//派生类对象初始化基类的引用
cout<<"rc.area()"<<rc.area()<<endl;//调用基类的area()

}

2.虚析构函数
目前C++标准不能设虚构造函数,有且只有一个虚析构函数

3.纯虚函数和抽象类
1>概念: 一个类可以声明多个纯虚函数,包含 纯虚函数 的类称为 抽象类 ,一个抽象类只能作为基类
来派生新类,不能声明抽象类对象(纯虚函数所占空间的大小不能确定故不能声明抽象类),
但可以声明指向抽象类的指针或引用,抽象类至少有一个虚函数是一个纯虚函数,
以便将它与空的虚函数区分开来;

virtual 函数类型 函数名(参数列表)=0;//纯虚函数;
virtual 函数类型 函数名(参数列表){};//空函数;

2>继承和派生:从一个抽象类派生的类必须提供纯虚函数的实现代码,或在该派生类中仍将它声明为
纯虚函数,此时说明了虚函数的派生类仍是抽象类,如果派生类给出了基类所有纯虚函数
的实现,则该派生类不再是抽象类;

3>类族
如果通过同一个基类派生一系列的类,则将这些类总称为类族,抽象类的这一特点保证类进入类族
的每个类都具有提供纯虚函数所要求的行为,进而保证围绕这个类族所建立的软件能正常运行;

4>在构造函数和析构函数内 不能调用一个纯虚函数 (不能静态开辟空间及释放空间),
在 构造函数 和 析构函数 中调用虚函数采用 静态联编 ,即它们调用的虚函数是自己类或基类中定义的函数
而不是任何派生类中重新定义的虚函数;(在构造和析构中的虚函数 自己有调自己的 , 自己没有调基类的 );

[e.g]
#include<iostream>
using namespace std;

//抽象类
class Shape{
public:
virtual double area()=0;//纯虚函数
};

//抽象类派生正方形类
class Square:public Shape{
protected:
double H;
public:
Square(double i){H=i;}
double area(){return H*H;}//重定义纯虚函数

};

//正方形类派生圆形类
class Circle:public Square{
public:
Circle(double r):Square(r){}
double area(){return H*H*3.14159;}//重定义纯虚函数

};

//正方形类派生出直角三角形类
class Triangle:public Square{
protected:
double W;
public:
Triangle(double h,double w):Square(h){W=w;}
double area(){return H*W*0.5;}//重定义纯虚函数

};

//直角三角形类派生出矩形类
class Rectangle:public Triangle{
public:
Rectangle(double h,double w):Triangle(h,w){}
double area(){return H*W;}//重定义纯虚函数

};

//计算总面积函数
double total(Shape *s[],int n){
double sum=0.0;
for(int i=0;i<n;i++){
sum+=s[i]->area();//复合赋值符,等价于sum=sum+s[i]->area();
cout<<"s["<<i<<"] ==>"<<s[i]->area()<<endl;
}
return sum;
}

int main(){
Shape *s[4];//定义shape类对象数组s,其内含有四个元素
//s[num]可向下转换为shape的派生类;多态性
s[0]=new Square(4);//定义正方形类对象
s[1]=new Circle(10);//定义圆形类对象
s[2]=new Triangle(3,6);//定义直角三角形对象
s[3]=new Rectangle(3,6);//定义矩形对象
double sum=total(s,4);//总面积
cout<<"总面积为"<<sum<<endl;
cin.get();
return 0;
}

5>类成员函数的指针与多态性
在派生类中,当一个指向基类成员函数的指针指向一个虚函数,并且通过指向对象
的基类指针访问这个虚函数时,仍然是动态联编(发生多态性)


第九章 运算符的重载及流类库
1.重载运算符的语法规范
1>一般为用户定义的类型重载运算符都要求能够访问这个类型的私有成员,为此可通过两种方法实现
*1.运算符重载为这个类型的成员函数-->类运算符;
*2.运算符重载为这个类型的友元函数-->友元运算符;
*3.使用 友元函数 重载运算符会比用 类成员函数 重载运算符
#1.会多一个对象的引用参数
#2.可以转换数据类型

*4.重载的运算符保持其原有的操作数,优先级和结合性不变;

2>C++运算符大部分都可以重载,不能重载的只有 "." , "::" , "*" , "? :" 和sizeof和#(不是运算符)

3>只能作为成员函数重载: "=" , "()" , "[]" , "->" ,这四个运算符

4>只能作为类的友元函数重载的: "<<" , ">>" ,这两种

2.使用operator关键字重载运算符
1> 重载"+"运算符
*1.使用友元函数重载"+"运算符
[e.g 重载"+"运算符]
#include<iostream>
using namespace std;
class complex{//声名复数类
private:
double real,imag;//声名复数的实部和虚部
public:
complex(double r=0,double i=0){real=r;imag=i;}//定义复数的构造函数
friend complex operator+(complex ,complex);//声名友元函数
void show(){cout<<real<<"+"<<imag<<"i"<<endl;}
};

//定义友元函数
complex operator+(complex a,complex b){
double r=a.real+b.real;//友元函数可以获取友元类的私有成员
double i=a.imag+b.imag;//友元函数可以获取友元类的私有成员
return complex(r,i);//获得相加后的新复数
}

void main(){
complex x(5,3),y;//y相当于y(0,0i);
y=x+7; //-->相当于调用了operator+(x,7);-->y(5+7,3i+0i)-->y.real=12;y.imag=3i;-->y=12+3i
y=7+y; //-->相当于调用了operator+(7,y);y(7+12,0i+3i)-->y.real=19;y.imag=3i-->y=19+3i
y.show();//输出19+3i
}
*2.使用类成员函数重载"+"运算符
#include<iostream>
using namespace std;
class complex{//声名复数类
private:
double real,imag;//声名复数的实部和虚部
public:
complex(double r=0,double i=0){real=r;imag=i;}//定义复数的构造函数
complex operator+(complex &b){
double r=real+b.real;
double i=imag+b.imag;
return complex(r,i);//获得相加后的新复数
}
void show(){cout<<real<<"+"<<imag<<"i"<<endl;}
};

void main(){
complex x(5,3),y(2,9);//y相当于y(0,0i);
y=x+y;//-->相当于调用了y=x.operator+(y)
y.show();//输出7+12i
}

2> 重载"++"运算符
*1.使用友元函数重载++运算符
[e.g 重载"++"运算符]
#include<iostream>
using namespace std;
class number{
int num;
public:
number(int i){num=i;}
friend int operator++(number&);//*1 一个参数为前缀++ ->++n;
friend int operator++(number&,int);//*2 两个参数为后缀++ ->n++; 虚构的第二参数以区分 n++还是++n;
void print(){cout<<"num=="<<num<<endl;}
};
int operator++(number&a){a.num++;return a.num;} //没做赋值运算,所以值是++n;
int operator++(number&a,int){int i=a.num++;return i;}//不用给出形参名,做了赋值运算i=a.num然后a.num++

void main(){
number n(10);
int i=++n;//这个调用的是*1
cout<<"i="<<i<<endl;//输出的是11
n.print();//num=11
i=n++;//这个调用的是*2
cout<<"i="<<i<<endl;//输出的是11
n.print();//num=12
}

*2.使用类成员函数重载"++"运算符
#include<iostream>
using namespace std;
class number{
int num;
public:
number(int i){num=i;}
int operator++();//*1 一个参数为前缀++ ->++n;
int operator++(int);//*2 两个参数为后缀++ ->n++; 虚构的第二参数以区分 n++还是++n;
void print(){cout<<"num=="<<num<<endl;}
};
int number:: operator++(){num++;return num;} //没做赋值运算,所以值是++n;
int number:: operator++(int){int i=num;num++;return i;}//不用给出形参名,做了赋值运算i=a.num然后a.num++
void main(){
number n(10);
int i=++n;//这个调用的是*1
cout<<"i="<<i<<endl;//输出的是11
n.print();//num=11
i=n++;//这个调用的是*2
cout<<"i="<<i<<endl;//输出的是11
n.print();//num=12
}


*.使用类运算符”++”运算符,使用函数调用方式,例↓:
若前缀:++n为n.operator++( );
若后缀:n++为n.operator++(0);,

3>重载字符串赋值运算符"="
*1.重载原因:
如类MyStr有数据成员是char *st
则MyStr s1("hello"),s2;s1=s2;经赋值后,s2.st和s1.st是同一块存储地址;
当s2和s1的生存期结束时,存储"hello"的变量将被删除两次,且s1=s1的情况不会被执行;
这时需要重载"=" ==>operator=(str&);使用"&"(使用引用不用再开辟空间可以节省内存)
赋值运算符"="只能用类成员函数重载;
*.字符串的赋值:scanf("%s",s1);字符串不用取地址,符输出为hello 如果是int a=1;
scanf("%d",&a);需要"&";

*2.str赋值运算符重载方法
#include<iostream>
#include<string>
using namespace std;
class MyStr{
private:
char *st;

public:
MyStr(MyStr& s){
cout<<"使用对象引用的构造函数"<<endl;
st=new char[strlen(s.st)+1];//为st申请内存
strcpy(st,s.st);//将对象s的字符串复制到内存区st中;
}

MyStr(char *s){
cout<<"使用字符指针的构造函数"<<endl;
st=new char[strlen(s)+1]; //为st申请内存,strlen方法返回长度不包括结束位'\0'
strcpy(st,s);//将字符串_st复制到内存区st中;
}

MyStr& operator=(MyStr& str){//重载赋值语句
cout<<"执行 operator=(MyStr& str)"<<endl;
if(this==&str){
cout<<"调用赋值运算符但不进行操作"<<endl;
return *this;//防止出现str=str这样的赋值
}
if(st !=NULL)delete st;//自身有内容,先释放空间
st=new char[strlen(str.st)+1];//重新申请内存
//C++源码:char * strcpy(char* destination,const char * source);
strcpy(st,str.st); //将对象str的字符串复制到本对象的st内存中
return *this; //返回this指针指向的对象
}

//传入字符串
MyStr& operator=(char*s){
cout<<"执行 operator=(char*s)"<<endl;
if(st !=NULL)delete st;//自身有内容,先释放空间
st=new char[strlen(s)+1];//重新申请内存
strcpy(st,s);//将字符串s复制到内存st中
return *this;//返回this指针指向的对象
}

//输出字符串
void display(){cout<<"字符内容为==>"<<st<<endl;}

~MyStr(){
cout<<"执行析构函数"<<endl;
if(st!=NULL){
cout<<"执行delete st"<<endl;
delete st;//释放空间
}
}
};

int main(){
//字符串本身就是char数组,不用取地址符
MyStr s1("hello"),s2("They"),s3(s1);//调用构造函数和复制构造函数
s1.display();s2.display();s3.display();
cout << "====================" << endl;
s2 =s1=s3;//==>调用复制操作符;
s3="Go home!";//调用字符串赋值操作符
s1.display();s2.display();s3.display();
s3=s3;//调用赋值运算符但不进行操作
cout << "=====================" << endl;
cin.get();
return 0;
}

4> 插入符"<<"和提取符">>"的重载(必须用类的友元函数重载)
*1.插入符函数的一般形式如下:
ostream &operator<<(ostream & output, 类名 & 对象名)
{ …… //函数代码
return output; }
output是类ostream对象的引用, 它是cout的别名, 即
Ostream & output=cout。 调用参数时, output引用cout(即cout的别名) ,
插入符函数的第二个参数可以使用对象名, 也可使用对象的引用。


*2.提取符函数的一般形式如下:
istream &operator>>(istream & input, 类名 & 对象名)
{ …… //函数代码
return input;
}
input是类istream对象的引用, 它是cin的别名, 即istream & input=cin。 调用参
数时, input引用cin(即cin的别名) 。
另外, 提取符函数需要返回新的对象值, 所以应该使用引用, 即“类名&对象名” ,
不能使用“类名 对象名” , 插入符函数不改变对象的值, 所以两种方法都可以。

[代码实现]
#include<iostream.h>
class test{
private:
int i;
float f;
char ch;
public:
test(int a=0,float b=0,char c='\0'){i=a;f=b;ch=c;}
friend ostream& operator<<(ostream&,test);
friend istream& operator>>(istream&,test&);
};

/**
* @param output 输出流对象引用
* @param obj test 对象
*/
ostream& operator<<(ostream& output,test obj){
output<<obj.i<<",";//output 是cout的别名
output<<obj.f<<",";
output<<obj.ch<<endl;
return output;
}

/**
* @param output 输入流对象引用
* @param obj test 对象
*/
istream& operator>>(istream& input,test& obj){
input>>obj.i;//input 是cin的别名
input>>obj.f;
input>>obj.ch;
return input;
}

void main(){
test A(1,2.5,'f');
cout<<(cout,A);
test B,C;
cout<<"input i f ch";
cin>>(cin,B);
cin>>(cin,C);
out<<(cout,B);
out<<(cout,C);
}

3.流类库的基础类
1>概念:
在C++中,输入输出是通过流完成的,把接收输出数据的地方称为目标,输入数据的出处称为源,
而输入输出操作可以看成字符序列在源,目标以及对象之间的流动.

C++将与输入和输出的操作定义为一个类体系,放在一个系统库里,以备用户调用,这个类体系
称为流类,提供这个流类实现的系统库称为流类库;

流类库的基础类在头文件<iostream>中说明,其中streambuf类管理一个流的缓冲区,普通用户
一般不涉及,而只使用ios类,istream类和ostream中提供的公有接口,完成流的提取和插入操作;

流是一个抽象概念,在实际进行I/O操作时,必须将流和一种具体的物理设备联接起来,
如cin联接键盘,cout,cerr和clog联接显示终端.

2>分类:
流类库预定义了4个流: cin , cout, cerr 和 clog,
事实上,可以将cin视作istream的一个对象,将cout视为ostream的一个对象;
ios_base类-->ios类-->istream类-->cin类-->iostream类
(虚基类)-->ostream类-->cout类-->iostream类
3>虚基类:
在C++中,如果在多条继承路径上有一个汇合处,则称该汇合处的基类为公共基类,
如ios类,由于可通过不同路径访问这个基类,从而使其产生多个实例,有时会引起二义性,
如果想使这个公共的基类只产生一个实例,就需要将它说明为虚基类;

*ios类就是istream和ostream的虚基类,用来提供对流进行格式化I/O操作和错误处理的
成员函数,用关键字virtual可以将公共基类声名为虚基类.
使用关键字virtual可以将公有基类定义为虚基类,虚基类只会产生基类的一个实例
[e.g]
#include <iostream>
using namespace std;
class x{
protected:
int a;
public:
x(){a=1;}
};

class x1:virtual public x{
public://在这里a=1+1=2 类x的实例只会有一个
x1(){a+=1;cout<<"x1: "<<a<<",";}
};

class x2:virtual public x{
public://这里的a=2+2=4
x2(){a+=2;cout<<"x2: "<<a<<",";}
};

class y:public x1,public x2{
public://这里的a=4
y(){cout<<"y:"<<a<<endl;}
};

void main(){
y obj;
cin.get();
}

4>默认输入输出格式控制
关于数值数据,默认方式是自动识别浮点数并用最短的方式输出,如将5.0作为5输出,输入3.4e+2
输出340等,还可以将定点数分成整数和小数部分,
例如"int a;double b;cin>>a>>b;",当用键盘输入23.45时,a为23,b为0.45;

*2.字符数据
①.单字符:默认方式是舍去空格,直到读到字符为止.例如"char a,b,c;"定义单字符对象
a,b和c,"cin>>a>>b>>c;",能将连续的3个字符分别正确的赋值给相应对象;

②.字符串:默认方式是从读到第一个字符开始,到空格符结束.例如"char s[10];" 定义
含有10个字符位置的字符串对象s,"cin>>s;"输入的如果是"abc def",则s内为abc;

③.字符数组:默认方式是使用数组名来整体读入,例如"char a[30]",使用cin>>a读入
"cout<<a;"输出;

④.字符指针:尽管为其动态分配了地址,也只能采取逐个赋值的方法,不仅不以空格
符结束,反而舍去空格,读到字符才计数;

⑤.因为字符串没有结束位,所以将其作为整体输出时,有效字符串后面将出现乱码,这时可
手工增加字符串结束符"\0"来消除乱码,例子如下:
char *p=new char[5];//申请5个字符所需要的内存地址
for(int i=0;i<4;i++) cin>>*(p+i);//假设输入w e andf
p[4]='\0';//将结束符'\0'赋给最后一个字符,不加会出现乱码;
cout<<p;//输出前四个字符wean;

5>文件流
*1.概念:在C++中,文件操作是通过流来完成的,C++共有输入文件流(ifstream类型的对象),
输出文件流(ofstream类型的对象)和输入输出文件流(fstream类型的对象)3种,
并已通过头文件<fstream>实现标准化;
[e.g]
#include<iostream>
#include<fstream> //输入流头文件
using namespace std;
void main(){
char ch[15],*p="abcdefg";
ofstream myFile;//建立输出流myFile
myFile.open("myText.txt");//建立输出流myFile和文件myText.txt之间的关联
myFile<<p;//使用输出流myFile将指针p所指字符串流向文件
myFile<<"GoodBye!";//使用输出流myFile直接将字符串流向文件
myFile.close();//关闭文件myText.text

ifstream getText("myText.txt");//建立输入流getText及其和文件myText.txt的关联并打开
for(int i=0;i<strlen(p)+8;i++)//使用输入流getText每次从文件myText.txt读入一个字符
getText>>ch[i];//将每次读入的1个字符赋给数组元素ch[i]
ch[i]='\0';//设置结束标志
getText.close(); //关闭文件
cout<<ch<<endl;//使用cout使数组元素流向屏幕,输出"abcdefgGoodByel"
cin.get();
}

*2.使用文件流
①.打开一个相应的文件流,
[e.g]
ofstream myStream;
myStream.open("myText.txt");//等价于以下语句
ofstream myStream("myText.txt");
//如果指定文件路径,路径中的"\"号必须使用转义字符表示,如
"ifstream getText("f:\\text\\myText.txt");//表示建立输入流文件ifstream和
路径为"f:\text\myText.txt"的文件关联;

②.及时关闭不再使用的文件流 ifstream.close();

③.几个典型流成员函数
ios_base::in 写 out 读

*3.open函数
ifstream inFile;
inFile.open("filename",iosmode);

ifstream *pInFile;
pInFile->open("filename");//指针输入流对象,指针;


第十章 面向对象实际设计
1.过程抽象和数据抽象
1>概念:抽象是从许多事务中舍弃个别的,非本质的特征,抽取共同及本质特征的过程;

2>抽象的意义
通过舍弃个体事务在细节上的差异,抽取其共同特征可以得到一些事务的抽象概念,
如OOA(面向对象设计)的类;

3>分为过程抽象和数据抽象;

2.接口继承与实现继承
1>成员函数的分类
类通过成员函数提供与外界的接口,成员函数分为实,虚,纯虚三种函数;
*1.纯虚函数(virtual double area()=0;)
纯虚函数最显著的两个特征是:
①.他们必须由继承它们的非抽象类重新说明;
②.它们抽象类中没有定义;//不能声明对象,只能声明指针
*声明一个纯虚函数的目的是使派生类继承这个函数的接口

*2.虚函数(动态联编)
虚函数(注意构造方法中的虚函数)看右边,实函数看左边//虚右不虚左
Point a(1.5,6.7);//基类
Circle c(1.5,6.7,2.5);//派生类
Point *p=&c;//派生类对象的地址赋值给指向基类的指针
p->area;//如果area()是虚函数那就是调用Circle的area(),否则就调用Point的;


#include <iostream>
#include <string.h>
#include <iomanip>
using namespace std;
class student{
char name[8];
int deg;
char level[7];
friend class task;
public:
student(char na[],int d){
strcpy(name,na);
deg=d;
}
};

class task{
public:
void trans(student &s){
int i=s.deg/10;
switch(i){
case 9:
strcpy(s.level,(char*)"优");
break;
case 8:
strcpy(s.level,(char*)"良");
break;

case 7:
strcpy(s.level,(char*)"中");
break;

case 6:
strcpy(s.level,(char*)"及格");
break;

}
}

void show(student &s){
cout<<setw(10)<<s.name<<setw(4)<<s.deg<<setw(8)<<s.level<<endl;
}
};

int main(){
student st[]={student((char*)"Jack",78),student((char*)"Lucy",92),
student((char*)"Lily",62),student((char*)"Tom",99)};
task p;
cout<<"结果:" <<"姓名"<<setw(6)<<"成绩"<<setw(8)<<"等级"<<endl;
for(int i=0;i<4;i++){
p.trans(st[i]);
p.show(st[i]);
}
return 0;
}

#include <iostream>
using namespace std;
class A{
public:
A(){}
~A(){cout<<"析构A"<<endl;}
};

class B{
public:
B(){}
~B(){cout<<"析构B"<<endl;}
};

class C{
private:
A a;
B b;
public:
C(){}
~C(){
/**
* 先执行~C()
* 再按声明本类成员对象的声明顺序的逆序调用其析构函数
* 调用b的析构函数 ~B();
* 调用a的析构函数~A();
*/
cout<<"析构C"<<endl;
}
};

int main(){
C c;
return 0;
}

 

posted @   R-Bear  阅读(67)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示