文章目录
- 1、const 用法:
- 2、sizeof 用法:
- 3、位运算:
- 4、类型转换操作符<类型说明符>(表达式):
- 5、常用的I/O流类库操纵符:
- 6、起别名:
- 7、枚举类型:
- 8、auto类型(编译器通过初始值自动判断变量类型)与decltype类型:
- 9、引用类型:
- 10、内联函数:
- 11、constexpr函数:
- 12、函数默认值:
- 13、函数重载:
- 14、类和对象:
- 15、前向引用声明:
- 16、UML:
- 17、结构体:
- 18、联合体:
- 19、枚举类:
- 20、作用域:
- 21、静态生存期:
- 22、类的友元:
- 23、常成员函数:
- 24、C++程序的一般组织结构:
- 25、条件编译指令——#if 和 #endif
- 26、循环终止输入方式(Ctrl+Z,Ctrl+C,Ctrl+D(EOF))
- 27、对象数组初始化:
- 28、基于范围的for循环:
- 29、指针:
- 30、函数指针(函数回调):
- 31、this指针:
- 32、动态分配与释放内存:
- 33、动态数组类:
- 34、智能指针:
- 35、vector对象:
- 36、深层复制和浅层复制:
- 37、移动构造:
- 38、string类、getline:
- 39、继承和派生
- 40、虚基类
- 41、运算符的重载
- 42、虚函数
- 43、虚表与动态绑定
- 44、纯虚函数、抽象类
- 45、override与final
- 46、函数模板、类模板
- 47、线性群体:
- 48、泛型程序设计
- 49、STL
- 50、迭代器
- 51、容器
- 52、函数对象
- 53、函数适配器
- 54、STL算法
- 55、IO流的概念及流类库的结构
1、const 用法:
const float PI = 3.1415926; // PI为只读变量,使用时必须初始化;
const float PI(3.1415926);//初始化时可使用;
2、sizeof 用法:
int x;
sizeof(short);
sizeof x;
3、位运算:
a = a & 0xfe; //将char型变量a的最低位 置 0;
c = a & 0xff; //有char c;int a;取出a的低字节;
a = a | 0xff; //将int型变量a的低字节 置 1;
01111010 ^ 00001111;//将特定位翻转;
4、类型转换操作符<类型说明符>(表达式):
const_cast; dynamic_cast; reinterpret_cast; static_cast;
5、常用的I/O流类库操纵符:
cout << dec //十进制表示 << hex 十六进制 << oct 八进制 << ws 提取空白符 << ends 插入空白符 << setprecision(int) 设置浮点数的小数位数 << setw(int) 设置域宽
6、起别名:
using Area = double;
typedef Area double;
7、枚举类型:
enum GameResult {WIN, LOSE, TIE, CANCEL};
//枚举类型为常量不能赋值,有默认值为 0,1,2,3...
//定义时可初始化默认值
GameResult result;
enum GameResult omit = CANCEL;
for(int count = WIN; count <= CANCEL; count++){
result = GameResult(count);
}
8、auto类型(编译器通过初始值自动判断变量类型)与decltype类型:
decltype(i) j = 2;//j的初始值为2,与i的类型一致
9、引用类型:
//引用(&)是标识符的别名
//定义一个引用时,必须同时对它进行初始化,使它指向一个已存在的对象
//一旦一个引用被初始化后,就不能改为指向其他对象
//引用一般用作形参
int i, j;
int &ri = i; //定义int引用ri,并初始化为变量i的引用
j = 10;
ri = j; //相当于 i = j, ri 和 i 是同一个变量
10、内联函数:
//内联函数就是将函数代码替换掉调用函数的语句,取消函数调用和返回的开销,编译器来实现
//内联函数体内不能有循环语句和switch语句
//内联函数的定义必须出现在内联函数第一次被调用之前
//对内联函数不能进行异常接口声明
#include <iostream>
using namespace std;
const double PI = 3.14159265358979;
inline double calArea(double radius){
return PI * radius * radius;
}
int main(){
double r = 3.0;
double area = calArea(r);
cout << area << endl;
return 0;
}
11、constexpr函数:
//constexpr修饰的函数在其所有参数都是constexpr时一定返回constexpr
constexpr int get_size(){
return 20;
}
int main(){
constexpr int foo = get_size();
// foo 是一个常量表达式
}
12、函数默认值:
//有默认参数的形参必须列在形参列表的最右,即默认参数值的右边不能有无默认参数值的参数;
//调用时形参与实参的结合次序时从左向右
//在调用函数之前,仅写一次
int add(int x = 5, int y = 6){
return x + y;
}
int main(){
add(10, 20);// 10 + 20
add(10); // 10 + 6
add(); // 5 + 6
}
13、函数重载:
// C++ 允许功能相近的函数在相同的作用域内以相同函数名声明,从而形参重载。方便使用,便于记忆
//重载函数的形参必须不同:个数不同或类型不同,返回值不能用来区别
int add(int x, int y);
float add(float x, float y);
int add(int x, int y, int z);
14、类和对象:
class Clock{
public:
Clock(int newH, int newM, int newS);//构造函数,用作本类成员初始化
Clock(); //Clock() = delete 不生成默认构造函数
Clock(Clock& p);//拷贝构造函数
~Clock(); //析构函数, 无返回类型,无return,不可有参数
//Clock(const Clock& p) = delete; 不生成默认复制构造函数
void setTime(int newH = 0, int newM = 0, int newS = 0);
void showTime();
//只允许本类中的函数访问,而类外部的任何函数都不能访问
private:
int hour, minute, second;
}
Clock::Clock(int newH, int newM, int newS):hour(newH), minute(newM), second(newS) {
} //无返回类型,无return语句
Clock::Clock() : hour(0), minute(0), second(0) {}
//Clock::Clock():Clock(0, 0, 0){} 委托构造函数
void Clock::setTime(int newH, int newM, int newS){
hour = newH;
minute = newM;
second = newS;
}
void Clock::showTime(){
cout << hour << ":" << minute << ":" << second << endl;
}
Clock::Clock(Clock& p) {
hour = p.hour;
minute = p.minute;
second = p.second;
}
Clock::~Clock(){}
void fun1(Clock newMyClock) {
cout << 111 << endl;
}
Clock fun2() {
Clock newClock;
return newClock;
}
int main(){
Clock myClock;
Clock yourClock(myClock);//用myClock初始化B。第一次调用拷贝构造函数
Clock c(0, 0, 0); // 自动调用构造函数
fun1(myClock); //对象myClock作为fun1的实参,用拷贝构造函数来完成临时对象newMyClock的初始化,第二次调用拷贝构造函数
myClock = fun2();//函数的返回值时类对象,函数返回时,临时对象newClock被析构,需要再构造一个临时对象this用来拷贝newClock并返回,因此调用拷贝构造函数
myClock.setTime(8, 30, 30);
myClock.showTime();
return 0;
}
15、前向引用声明:
// 在提供一个完整的类声明之前,不能声明该类的对象,也不能在内联成员函数中使用该类的对象
// 当使用前向引用声明时,只能使用被声明的符号,而不能涉及类的任何细节
class B;
class A{
public:
void f(B b);
B d;//错误,编译器不能处理d的细节,需用 b *d;
};
class B{
public:
void g(A a);
A a;
};
16、UML:
//可视化面向对象的建模语言
//UML有三个基本部分:事物、关系、图
17、结构体:
//可以有数据成员和函数成员
//如果:
// 一个结构体的全部数据成员都是公有成员;
// 没有用户定义的构造函数
// 没有基类和虚函数
// 则可以使用下面的语法形式初始化
// 类型名 变量名 = {初值1,初值2,......}
struct Clock{
int hour; //公有成员
protected:
int minute;//保护型成员
private:
int second;//私有成员
};
18、联合体:
// 成员共用同一组内存单元
// 任何两个成员不会同时有效
/*union Clock {
int hour; //公有成员
protected:
int minute;//保护型成员
private:
int second;//私有成员
};
union Mark{
char grade; //等级制的成绩
bool pass; //只记是否通过课程的成绩
int percent; //百分制成绩
};
union {
int i;
float j;
};//无名联合体*/
//Example
#include <srting>
#include <iostream>
using namespace std;
class ExamInfo {
private:
string name;//课程名称
enum { GRADE, PASS, PERCENTAGE } mode;//计分方式
union{
char grade; //等级制
bool pass; //是否通过
int percent;//百分制
};
public:
ExamInfo(string name, char grade):name(name), mode(GRADE), grade(grade) {}
ExamInfo(string name, bool pass):name(name), mode(PASS), pass(pass){}
ExamInfo(string name, int percent):name(name), mode(PERCENTAGE), percent(percent) {}
void show();
}
void show(){
cout << name << ":";
switch (mode) {
case GRADE:
cout << grade << endl;
break;
case PASS:
cout << pass << endl;
break;
case PERCENTAGE:
cout << percent << endl;
break;
}
}
int main() {
ExamInfo course1("English", 'B');
ExamInfo course2("Calculus", true);
ExamInfo course3("C++ Programming", 85);
course1.show();
course2.show();
course3.show();
return 0;
}
19、枚举类:
//强作用域:其作用域限制在枚举类中(使用Tyoe的枚举值General:Type::General)
//转换限制:枚举类对象不可以与整型隐式地互相转换
//可以指定底层类型
enum class Type {General, Light, Medium, Heavy}; //默认所有枚举常量均为int
enum class Type:char{General, Light, Medium, Heavy}; //所有枚举常量均为char
enum class Category{General = 1, Pistol, MachineGun, Cannon};//枚举常量为1,2,3,4
//Example
#include<iostream>
using namespace std;
enum class Side {Right, Left};
enum class Thing {Wrong, Right}; //不冲突
int main() {
Side s = Side::Right;
Thing w = Thing::Wrong;
cout << (s == w) << endl; //编译错误,无法直接比较不同枚举类
return 0;
}
20、作用域:
//类作用域:
//类的成员具有类作用域,其范围包括类体和成员函数体
//在类作用域以外访问类的成员:
// 静态成员:通过类名,或者该类的对象名,对象引用访问
// 非静态成员:通过类名,或者该类的对象名,对象引用,对象指针访问
//文件作用域(全局作用域):
//不在前述各个作用域(类,{})中出现的声明,就具有文件作用域
//其作用域开始于声明点,结束于文件尾
int i;//全局变量,文件作用域
int main(){
i = 5;//全局变量i赋值
{
int i;//局部变量,局部作用域
i = 7;
cout << i << endl; //输出7
}
cout << i << endl; //输出5
return 0;
}
21、静态生存期:
//这种生存期与程序的运行期相同
//在文件作用域中声明的对象具有这种生存期。
//在函数内部声明静态生存期对象要冠以关键字static
int i = 1;
void other(){
static int a = 2; //全局寿命,局部可见
static int b;
int c = 10;
a += 2;
b = a;
}
//类静态数据成员:
//用关键字static声明
//为该类的所有对象共享,静态数据成员具有静态生存期
//必须在类外定义和初始化,用(::)来指明所属的类。
class Point{
public:
Point(int x = 0; int y = 0):x(x), y(y){
count++;
}
Point(Point &p){
x = p.x; y = p.y; count++;
}
~Point(){ count--; }
int getX(){ return x; }
int getY(){ return y; }
void showCount(){
cout << " Object count = " << count << endl;
}
private:
int x, y;
static int count;
};
int Point::count = 0; //静态数据成员定义和初始化,使用类名限定
int main(){
Point a(4, 5); //定义对象b,其构造函数会使count加1
cout << "Point B:" <<b.getX() << "," << b.getY();
b.showCount(); //输出对象个数
return 0;
}
22、类的友元:
//友元是C++提供的一种破坏数据封装和数据隐藏的机制
//通过将一个模块声明为另一个模块的友元,一个模块能够引用到另一个模块中本事被隐藏的信息
//可以声明友元函数和友元类
//为了确保数据的完整性,及数据封装与隐藏的原则,建议慎用友元
//友元函数是在类声明中由关键字friend修饰说明的非成员函数,在它的函数体中能够通过对象名访问private 和protected成员
//作用:增加灵活性,使程序员可以在封装和快速性方面做合理选择
class Point{
public:
Point(int x = 0; int y = 0):x(x), y(y){
}
Point(Point &p){
x = p.x; y = p.y; count++;
}
~Point(){ count--; }
int getX(){ return x; }
int getY(){ return y; }
friend float dist(const Point& a, const Point& b);//常引用,值不可被修改
private:
int x, y;
};
float dist(const Point& a, const Point& b);{ //常引用,值不可被修改
double x = a.x - b.x;
double y = a.y - b.y;
return static_cast<float>(sqrt(x*x + y*y));
}
pr
int main(){
Point p1(1, 1), p2(4, 5);
cout << "The distance is : ";
cout << dist(p1, p2) << endl;
return 0;
}
// 友元类
class A{
friend class B; //B类是A类的友元 != A类是B类的友元
public:
void display(){
cout << x << endl;
}
private:
int x;
};
class B{
public:
void set(int i);
void display();
private:
A a;
};
void B::set(int i){
a.x = i;
}
void B::display(){
a.display();
}
23、常成员函数:
class R{
public:
R(int r1, int r2):r1(r1), r2(r2){}
void print();
void print() const;
private:
int r1, r2;
};
void R::print(){
cout << r1 << "," << r2 << endl; //可不写,不是常对象也会调用常函数
void R::print()const{
cout << r1 << "," << r2 << endl;
}
int main(){
R a(3, 4);
a.print(); //调用void print()
const R b(4, 5);
b.print(); //调用 void print() const
return 0;
}
24、C++程序的一般组织结构:
//一个工程可以划分为多个源文件,例如:
// 类声明,定义文件(.h文件)
// 类成员函数实现文件(.cpp文件)
// 类的使用文件(main()所在的.cpp文件)
//利用工程来组合各个文件
//Example
//文件1,类的定义,Point.h
class Point{
public:
Point(int x = 0; int y = 0):x(x), y(y){
count++;
}
Point(Point &p){
x = p.x; y = p.y; count++;
}
~Point(){ count--; }
int getX(){ return x; }
int getY(){ return y; }
void showCount(){
cout << " Object count = " << count << endl;
}
private:
int x, y;
static int count;
};
//文件2,类的实现,Point.cpp
#include "Point.h" //""首先在当前目录下寻找文件
#include <iostream> //<>首先在文件安装目录下寻找文件
using namespace std;
int Point::count = 0; //静态数据成员定义和初始化,需加上"类名::"来修饰
Point::Point(const Point &p):x(p.x), y(p.y){
count++;
}
void Point::showCount(){
cout << " Object count = " << count << endl;
}
//文件3,主函数,test.cpp
#include "Point.h"
#include <iostream>
using namespace std;
int main(){
Point a(4, 5); //定义对象b,其构造函数会使count加1
cout << "Point B:" << b.getX() << "," << b.getY();
b.showCount(); //输出对象个数
return 0;
}
//point.h, iostream 文件包含进test.cpp, point,cpp文件中
//point.cpp文件编译形成point.obj文件
//test.cpp文件编译形成test.obj文件
//系统运行库,point.obj, test.obj 文件连接形成可执行文件 test.exe
25、条件编译指令——#if 和 #endif
//#if (常量表达式)
//当“常量表达式”非零时编译
程序正文
//#endif
//条件编译指令——#else
//#if 常量表达式
...
//#else
//当“常量表达式”为零时编译
//......
//#endif
//条件编译指令:
#ifdef 标识符
程序段1// 标识符未被定义过
#else
程序段2
#endif
26、循环终止输入方式(Ctrl+Z,Ctrl+C,Ctrl+D(EOF))
//ctrl-c 发送 SIGINT 信号给前台进程组中的所有进程。常用于终止正在运行的程序。
//ctrl-z 发送 SIGTSTP 信号给前台进程组中的所有进程,常用于挂起一个进程。
//ctrl-d 不是发送信号,而是表示一个特殊的二进制值,表示 EOF。
int c;
while(cin >> c){
cout << "输入ctrl z 停止输入字符 c " << endl;
}
27、对象数组初始化:
Point a[2] = {Point(1, 2), Point(3, 4)};//若没有为数组元素指定显式初始值,数组元素默认使用默认值初始化(调用默认构造函数)
28、基于范围的for循环:
int main(){
int array[3] = {1, 2, 3};
for(int & e:array){
e += 2;
std::cout << e << std:: endl;
}
return 0;
}
29、指针:
int *p = nullptr //初始化为空指针,此时p == 0
void *p; //可指向任意数据类型的指针
int * const p2 = &a; p2 = &b; //错误,p2是指针常量不可赋值
//指针类型运算的结果值取决于指针指向的数据类型,总是指向一个完整数据的起始位置
30、函数指针(函数回调):
#include <iostream>
using namespace std;
int compute(int a, int b, int(*func)(int, int)){
return func(a, b);
}
int max(int a, int b){
return (a>b?a:b);
}
int min(int a, int b){
return (a<b?a:b)
}
int sum(int a, int b){
return a+b;
}
int main(){
int a, b, res;
cout << "请输入整数a:"; cin >> a;
cout << "请输入整数b:"; cin >> b;
res = compute(a, b, &max);
cout << "Max of" << a << "and" << b >> "is" << res << endl;
res = compute(a, b, &min);
cout << "Min of" << a << "and" << b >> "is" << res << endl;
res = compute(a, b, & sum);
cout << "Sum of" << a << "and" << b >> "is" << res << endl;
}
31、this指针:
//隐含于类的每一个非静态成员函数中
//指出成员函数所操作的对象
//当通过一个调用成员函数时,系统先将该对象的地址赋给this指针,然后调用成员函数,成员函数对对象的数据成员进行操作时,就隐含使用了this指针
//Example, Point类的getX函数中的语句:
return x; //相当于return this->x;
//b.getX(),将b的地址赋给this指针,然后进入函数内部,返回this指针的x;若无this指针,进入函数内部后不知道应该访问那个对象的x
32、动态分配与释放内存:
//动态申请内存操作符 new
//new 类型名T(初始化参数列表)
//功能:在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋以初值
//结果:T类型的指针,指向新分配的内存;失败:抛出异常
int main(){
cout << "Step one: " << endl;
Point *ptr1 = new Point; //调用默认构造函数
delete prt1;
cout << "Step two: " << endl;
ptr1 = new Point(1, 2);
delete ptr1;
Point *ptr = new Point[2];
delete[] ptr; //释放整个数组
}
33、动态数组类:
#include <iostream>
#include <cassert>
using namespace std;
class Point{ /*类的声明同上*/ };
class ArrayOfPoints{
public:
ArratOfPoints(int size):size(size){
points = new Point[size];
//int **a;
//a = new int*[3];
//for(int i = 0; i < 3; i++){
// a[i] = new int[3];
//}
//构建动态二维数组
}
~ArrayOfPoints(){
cout << "Deleting..." << endl;
delete[] points;
}
Point& element(int index){
assert(index >= 0 && index < size); //检查指针是否越界
return points[index]; //返回 引用 可以用来操作封装数组对象内部的数组元素。如果返回 值 则只是返回了一个副本,通过副本是无法操作原来数组的元素的
}
private:
Point *points;
int size;
};
int main(){
int count;
cout << "Please enter the count of points: ";
cin >> count;
ArrayOfPoints points(count); //创建数组对象
points.element(0).move(5, 0);
points.element(1).move(15, 20);
return 0;
}
34、智能指针:
unique_ptr : //不允许多个指针共享资源,可以用标准库中的move函数转移指针
shared_ptr : //多个指针共享资源
weak_ptr : //可复制shared_ptr,但其构造或者释放对资源不产生影响
35、vector对象:
// 封装任何类型的动态数组,自动创建和删除
// 数组下标越界检查
vector<int> arr(5); //建立大小为5的整形数组
// vector对象的使用:
// vector对象名[下标表达式]
// vector数组对象名不表示数组首地址
//获得数组长度用size函数, vector对象名.size()
int main(){
unsigned n;
cout << "n = ";
cin >> n;
vector<double> arr(n);
cout << "Please input" << n << " real numbers: " << endl;
for(unsigned i = 0; i < n; i++)
cin >> arr[i];
cout << "Average = " << average(arr) << endl;
return 0;
}
//基于范围的for循环配合auto举例
#include <vector>
#include <iostream>
int main(){
std::vector<int> v = {1, 2, 3};
for(auto i = v.begin(); i != v.end(); i++){
std::cout << *i << std::endl;
}
for(auto e : v){
std::cout << e << std::endl;
}
}
36、深层复制和浅层复制:
//默认的复制构造函数时浅层复制:定义一个指针,指向该需要复制的单元
//深层复制需要自己实现:重新创建一个副本,将需要复制的单元复制进去
37、移动构造:
class IntNum{
public:
IntNum(int x = 0):xptr(new int(x)){
cout << "Calling constructor..." << endl;
}
IntNum(const IntNum & n):xptr(new int(*n.xptr)){
cout << "Calling copy constructor..." << endl;
}
IntNum(IntNum && n):xptr(n.xptr){ //&& 是右值引用,函数返回的临时变量(即将消亡的)是右值
n.xptr = nullptr;
cout << "Calling move constructor..." << endl;
}//移动构造效率更高,相当于将原对象的资源转给临时对象
~IntNum(){
delete xptr;
cout << "Destructing..." << endl;
}
private:
int *xptr;
};
IntNum getNum(){
IntNum a;
return a;
}
int main(){
cout << getNum().getInt() << endl;
return 0;
}
38、string类、getline:
//string(); 默认构造函数,建立一个长度为0的串
string s1;
string(const char *s); //用指针s所指向的字符串常量初始化string对象
string s2 = "abc";
string(const string& rhs); //复制构造函数
string s3 = s2;
//string类的常用操作:
//s + t 将s和t连接成一个新串
//s = t 用t更新s
//s == t 判断s与t是否相等
//s != t 判断s与t是否不等
//s < t 判断s是否小于t(按字典顺序比较,下同)
//s <= t
//s > t
//s >= t
//s[i] 访问串中下标为i的字符
//getline可以输入整行字符串(要包string头文件)
//Example:
getline(cin, s2);
//输入字符串时,可以使用其他分隔符作为字符串结束的标志(例如逗号,分号),将分隔符作为getline的第三个参数即可
//Example:
getline(cin, s2. ',');
int main(){
for(int i = 0; i < 2; i++){
string city, state;
getline(cin, city, ',');
getline(cin, state);
cout << "City:" << city << " State:" << state << endl;
}//输入:beijinh,china ,city = beijing, state = china
}
39、继承和派生
//继承与派生是同一个过程从不同的角度看:
// 保持已有类的特性而构造新类的过程称为继承。 注重保持特性
// 在已有类的基础上新增自己的特性而产生新类的过程称为派生。 注重增加新类
//被继承的已有类称为基类(或父类)
//派生出的新类称为派生类(或子类)
//直接参与派生出某类的基类称为直接基类。
//基类的基类甚至更高层的基类称为间接基类。
//派生类的析构函数执行之后会自动调用基类的析构函数
//单继承时派生类的定义:
class Derived:public Base{ //派生类名:继承方式 基类名
public: //新生成员
Derived();
~Derived();
};
//多继承时派生类的定义:
class Derived:public Base1, private Base2{
public:
Derived();
~Derived();
};
//派生类的构成:
//1、吸收基类成员 2、改造基类成员 3、添加新的成员
//继承方式:1、公有继承 2、私有继承 3、保护继承
//1、公有继承(public):
// 继承的访问控制
// 基类的public和protected成员:访问属性在派生类中保持不变;
// 基类的private成员:不可直接访问
// 访问权限
// 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员
// 通过派生类的对象只能访问public成员
//2、私有继承(private):
// 继承的控制访问
// 基类的public和protected成员都以private身份出现在派生类中;
// 基类的private成员不可直接访问
// 访问权限
// 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员
// 通过派生类的对象不能直接访问从基类继承的任何成员
//3、保护继承(protected)
// 继承的访问控制
// 基类的public和protected成员都以protected身份出现在派生类中
// 基类的private成员不可直接访问
// 访问权限
// 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员
// 通过派生类的对象不能直接访问从基类继承的任何成员
//protected成员的特点和作用:
// 对建立其所在类对象的模板来说,它与private成员的性质相同
// 对于其派生类来说,它与public成员的性质相同
// 既实现了数据隐藏,又方便继承,实现代码重用
//类型转换:
// 公有派生类对象可以被当作基类的对象使用,反之则不可。
// 派生类的对象可以隐含转换为基类对象
// 派生类的对象可以初始化基类的引用
// 派生类的指针可以隐含转换为基类的指针,基类的指针可指向派生类的对象
// 通过基类对象名、指针只能使用从基类继承的成员
class Base1{
public:
void display()const{
cout << "Base1::display()" << endl;
}
};
class Base2:public Base1{
public:
void display()const{
cout << "Base2::display()" << endl;
}
};
class Derived:public Base2{
public:
void display() const{
cout << "Derived::display()" << endl;
}
};
void fun(Base1 *ptr){
ptr->display();
}
int main(){
Base1 base1;
Base2 base2;
Derived derived;
fun(&base1);
fun(&base2);
fun(&derived);
}//运行结果均调用Base1的display函数
//不要重新定义继承而来的非虚函数
//using B::B; 可以从基类继承构造函数,但不可为派生类新增的数据成员初始化
//单继承时构造函数的定义语法:
//派生类名::派生类名(基类所需的形参,本类成员所需的形参):基类名(参数表),本类成员初始化参数表
{
//其他初始化
}
//多继承时,需要将所有基类所需的形参写进参数列表,若没有给基类的构造函数传递参数,则默认基类调用无参构造函数
//构造函数的执行顺序:
// 1、调用基类构造函数,舒徐按照它们被继承时声明的顺序(从左向右)
// 2、对初始化列表中的成员进行初始化
// 顺序按照他们在类中定义的顺序
// 对象成员初始化时自动调用其所属类的构造函数
// 由初始化列表提供参数
// 3、执行派生类的构造函数体中的内容
//基类的构造函数先执行,然后是成员初始化的构造函数
//成员先析构,基类再析构
40、虚基类
//虚基类解决的问题:
// 当派生类从多个基类派生,而这些基类又有共同基类,则在访问此共同基类中的成员时,将产生冗余,并有可能因冗余带来不一致性。
//虚基类声明:
// 以virtual说明基类继承方式:
class B1:virtual public B
//作用:
// 主要用来解决多继承时可能发生的对同一基类继承多次而产生的二义性问题;
// 为最远的派生类提供唯一的基类成员,而不重复产生多次复制
// 注意:在第一级继承时就要将共同基类设计为虚基类,本质为继承指针
#include <iostream>
using namespace std;
class Base0{
public:
int var0;
void fun0(){ cout << "Member of Base0" << endl;}
};
class Base1:virtual public Base0{
public:
int var1;
};
class Base2:virtual public Base0{
public:
int var2;
};
class Derived:public Base1, public Base2{
//定义派生类Derived
public:
int var;
void fun(){
cout << "Member of Derived" << endl;
}
};
int main(){
Derived d;
d.var0 = 2; //直接访问虚基类的数据成员
d.fun0(); //直接访问虚基类的函数成员
return 0;
}
//有虚基类时的构造函数举例:
class Base0{
public:
base0(int var):var0(var){}
int var0;
void fun0(){cout << "Member of Base0" << endl;}
};
class Base1:virtual public Base0{
public:
Base1(int var):Base0(var){}//传递给基类的构造函数的参数被忽略
int var1;
};
class Base2:virtual public Base0{
public:
Base2(int var):Base0(var){}//传递给基类的构造函数的参数被忽略
int var2;
};
class Derived:public Base1, public Base2{
public:
Derived(int var):Base0(var), Base1(var), Base2(var){};
int var;
void fun(){cout << "Member of Derived" << endl;}
};
41、运算符的重载
//C++几乎可以重载全部的运算符,而且只能够重载C++中已经有的 // 不能重载的运算符:“.“、”*“、”::“、”?:“
// 重载后运算符的优先级和结合性都不会改变 //重载为类的非静态成员函数: // 函数类型 operator 运算符(形参){} //重载为非成员函数:
//双目运算符重载规则:
// 如果要重载B为类成员函数,使之能够实现表达式 oprd1 B oprd2,其中oprd1为A类对象,则B应被重载为A类的成员函数,形参类型应该是oprd2所属类型
// 经重载后,表达式oprd1 B oprd2 相当于 oprd1.operator B(oprd2)
class Complex{
public:
Complex operator + (const Complex &c2) const;
Complex operator - (const Complex &c2) const;
void display() const; //输出复数
Complex(double r = 0.0, double i = 0.0):real(r), imag(i){}
private:
double real; //复数实部
double imag; //复数虚部
};
Complex Complex::operator + (const Complex &c2) const{
//创建一个临时无名对象作为返回值
return return Complex(c2.real+real, c2.imag+imag);
}
Complex Complex::operator - (const Complex &c2) const{
return Complex(real-c2.real, imag-c2.imag);
}
void Complex::display() const{
cout << "(" << real << "," << imag << ")" << endl;
}
//前置单目运算符重载规则:
// 如果 要重载 U 为类成员函数,使之能够实现表达式 U oprd,其中 oprd 为A类对象,则 U 应被重载为 A 类的成员函数,无形参
// 表达式 U oprd 相当于 opr.operator U()
//后置单目运算符重载规则:
// 如果 要重载 U 为类成员函数,使之能够实现表达式 oprd U,其中 oprd 为A类对象,则 U 应被重载为 A 类的成员函数,比前置单目运算符多一个形参 0, 用以区分前置单目运算符
42、虚函数
//用virtual关键字说明的函数
//虚函数是实现运行时多态性基础
//C++中的虚函数是动态绑定的函数
//虚函数必须是非静态的成员函数,虚函数经过派生之后,就可以实现运行过程中的多态。虚函数指示编译器不要再编译时静态绑定函数,将虚函数接口放入虚表中,再运行时通过指针确定指向的对象的虚表指针从而进行动态绑定,因此虚函数属于对象而不属于类,则必须时非静态的成员函数
//构造函数不可以是虚函数,但析构函数可以
class Base1{
public:
virtual void display() const; //虚函数,需在类外实现
};//可用于解决39中的多态性问题
//一般虚函数成员:
// 虚函数的声明
// virual 函数类型 函数名(形参表)
// 虚函数声明只能出现在类定义中的函数原型声明中,而不能在成员函数实现的时候
// 在派生类中可以对基类中的函数成员函数进行覆盖,从而实现多态性
// 虚函数一般不声明为内联函数,因为对虚函数的调用需要动态绑定,而对内联函数的处理是静态的
//virual 关键字:
// 派生类可以不显示的用virtual声明虚函数,这是系统就会用以下规则来判断派生类的一个函数成员是不是虚函数:
// 该函数是否与基类的虚函数有相同的名称、参数个数及对应参数类型;
// 该函数是否与基类的虚函数有相同的返回值或者满足类型兼容规则的指针、引用型的返回值;
// 如果从名字、参数及返回值三个方面检查之后,派生类的函数满足上述条件,就会自动确定为虚函数。这时,派生类的虚函数便覆盖了基类的虚函数
// 派生类中的虚函数还会隐藏基类中同名函数的所有其它重载形式,需要使用时需加::
// 一般习惯于在派生类的函数中也使用virtual关键字,以增加程序的可读性。
//虚析构函数:
class Base{
public:
virtual ~Base(); //不是虚函数
};
Base::~Base(){
cout << "Base destructo" << endl;
}
class Derived:public Base{
public:
Derived();
virtual ~Derived();
private:
int *p;
};
Derived::Derived(){
p = new int(0);
}
Derived::~Derived(){
cout << "Derived destructor" << endl;
delete p;
}
void fun(Base* b){
delete b; //静态绑定,只会调用~Base()
}
int main(){
Base * b = new Derived();
fun(b);
return 0;
}
43、虚表与动态绑定
//虚表:
// 每一个类有一个虚表(virtual table),将虚函数放入
// 虚表中有当前类的各个虚函数的入口
// 每个对象有一个指向当前类的虚表的指针(虚指针vptr)
//动态绑定的实现:
// 构造函数中为对象的虚指针赋值
// 通过多态类型的指针或引用调用成员函数时,通过虚指针找到虚表,进而找到所调用的虚函数的入口地址
// 通过该入口地址调用虚函数
44、纯虚函数、抽象类
//纯虚函数:
// 纯虚函数时一个在基类中声明的虚函数,它在该基类中没有定义具体的操作内容,要求各派生类根据实际需要定义自己的版本,纯虚函数的声明格式为:virtual 函数类型 函数名(参数表) = 0;表示无函数体
//抽象类:包含纯虚函数的类,不能定义对象,只能做基类用,规范类家族的统一对外接口的形式,已经必须具有的功能
//Example:
class 类名{
virtual 类型 函数名(参数表) = 0;//纯虚函数
//其他成员......
}
/-----------------------------------/
class Base1{
public:
virtual void display() const = 0; //纯虚函数
};
class Base2:public Base1{
public:
virtual void display() const; //覆盖基类的虚函数
};
void Base2::display() const{
cout << "Base2::display()" << endl;
}
class Derived:public Base2{
public:
virtual void display()const;
};
void Derived::display()const;{
cout << "Derived::display()" << endl;
}
void fun(Base1 *ptr){
ptr->display();
}
int main(){
Base2 base2;
Derived derived;
fun(&base2);
fun(&derived);
return 0;
}
45、override与final
//overide:
// 多态行为的基础:基类声明虚函数,派生类声明一个函数覆盖该虚函数;
// 覆盖要求:函数签名(signature)完全一致
// 函数签名包括:函数名 参数列表 const
// 在虚函数显式重载中运用,编译器会检查基类是否存在一虚拟函数,与派生类中带有声明overide的虚拟函数,有相同的函数签名(signature);若不存在,则会汇报错误
//final:
struct Base1 final{};
struct Derived1:Base1{}; //编译错误:Base1为final,不允许被继承
struct Base2{
virtual void f() final;
};
sturct Derived2:Base2{
void f(); //编译错误:Base2::f为final,不允许被覆盖
};
46、函数模板、类模板
//函数模板定义语法
// template<模板参数表>
// 函数定义
//模板参数表内容:
// 类型参数:class(typename)标识符
// 常量参数:类型说明符 标识符
// 模板参数:template <参数表> class 标识符
//Example:
template<class T>
void outputArray(const T *array, int count){
for(int i = 0; i < count; i++)
cout << arrau[i] << " ";
cout << endl;
}
template<typename T>
T abs(T x){
return x < 0?-x:x;
} //根据实参 推导出 第一次调用时T类型应为int,调用的实际上是int abs(int x)
int main(){
int n = -5;
double d = -5.5;
cout << abs(n) << endl;
cout << abs(d) << endl;
return 0;
}
//-------注意:
//一个函数模板并非自动可以处理所有类型的数据
//只能够进行函数模板中运算的类型可以作为类型实参
//自定义的类,需要重载模板中的运算符,才能作为类型实参
//类模板的作用:
// 使用类模板使用户可以为类声明一种模式。使得类中的某些数据成员、某些成员函数的参数、某些成员函数的返回值,能取任意类型(包括基本类型的和用户自定义类型)
//类模板的声明:
template<模板参数表>
class类名
{类成员声明}
//如果需要在类模板意外定义其成员函数,则要采用一下的形式:
template<模板参数表>
类型名 类名<模板参数标识符列表>::函数名(参数表)
// -------Example
struct Student{
int id;
float gpa;
};
template <class T>
class Store{
private:
T item;
bool haveValue;
public:
Store();T &getElem();
void putElem(const T &x);
};
template <class T>
Store<T>::Store():haveValue(false){}
template <class T>
T &Store<T>::getElem(){
//如试图提取未初始化的数据,则终止程序
if(!haveValue){
cout << "No item present!" << endl;
exit(1);
}
return item; //返回item中存放的数据
}
template <class T>
void Store<T>::putElem(const T &x){
//将haveValue置为true, 表示item中已存入数值
haveValue = true;
item = x; //将x值存入item
}
int main(){
Store<int> s1, s2;
s1.putElem(3);
s2.putElem(-7);
cout << s1.getElem() << " " << s2.getElem() << endl;
Student g = {1000, 23};
Store<Student> s3;
s3.putElem(g);
cout << "The student id is" << s3.getElem(),id << endl;
Store<double> d;
cout << "Retrieving object D...";
cout << d.getElem() << endl;
//d末初始化,执行函数D.getElement()时导致程序终止
return 0;
}
47、线性群体:
//群体的概念:
//群体是指由多个数据元素组成的集合体
// 群体可以分为两个大类:线性群体和非线性群体
// 线性群体中的元素按位置排列有序,可以区分为第一个元素、第二个元素等,线性群体中的元素次序与逻辑位置关系是对应的。在线性群体中,又可按照访问元素的不同方法分为直接访问、顺序访问和索引访问。
// 非线性群体不用位置顺序来标志元素
//1、直接访问的线性群体:数组类
template<class T> //数组类模板定义
class Array{
private:
T *list;
int size;
public:
Array(int sz = 50);
Array(const Array<T> &a);
~Array();
Array<T> &operator = (const Array<T> &rhs); //重载“ = ”
T& operator[](int i); //重载“ 【】 ”
const T& operator[](int i) const; //重载“【】”常函数
operator T* ();
operator const T* () const;
int getSize() const;
void resize(int sz);
};
template<class T> Array<T>::Array(int sz){ //构造函数
assert(sz >= 0); //sz为数组大小(元素个数),应该非负
size = sz; //将元素个数赋值给变量size
list = new T[size]; //动态分配size个T类型的元素空间
}
template <class T> Array<T>::~Array(){
delete [] list;
}
template<class T> Array<T>::Array(const Array<T> &a){
size = a.size;
list = new T[size];
for(int i = 0; i < size; i++)
list[i] = a.list[i];
}
template<class T> Array<T>::~Array(){
delete[] list;
}
template<class T>
Array<T>::Array(const Array<T> &a){
size = a.size;
list = new T[size];
for(int i = 0; i < size; i++)
list[i] = a.list[i];
}
template <class T> Array<T> &Array<T>::operator = (const Array<T> &rhs){
if(&rhs != this){
if(size != rhs.size){
delete[] list;
size = rhs.size;
list = new T[size];
}
//从对象X复制数组元素到本对象
for(int i = 0; i < size; i++)
list[i] = rhs.list[i];
}
return *this; //返回当前对象的引用
}
template <class T> Array<T> &Array<T>::operator [](int n){
assert(n >= 0 && n < size); //检查下标是否越界
return list[n]; //返回下标为n的数组元素
}
template<class> const T &Array<T>::operator[] (int n) const{
assert(n >= 0 && n < size); //检查下标是否越界
return list[n]; //返回下标为n的数组元素
}
//重载指针转换运算符,将Array类的对象名转换为T类型的指针
template<class T> Array<T>::operator T* (){ //语法要求:无返回类型
return list;
}
//取当前数组的大小
template<class T> int Array<T>::getSize() const{
return size;
}
//将数组大小修改为sz
template<class T>
void Array<T>::resize(int sz){
assert(sz >= 0); //检查sz是否非负
if(sz == size) //若指定的大小与原有大小相同,则返回
return;
T* newList = new T[sz];
int n= (sz < size)? sz:size;
for(int i = 0; i < n; i++)
newList[i] = list[i];
delete[] list;
list = newList; //删除原数组
size = ze; //更新size
}
Array类的应用(2-N的质数判断)
int main(){
Array<int> a(10);
int n, count = 0;
cout << "Enter a value >= 2 as upper limit for prime numbers";
cin >> n;
for(int i = 2; i <= n; i++){
bool isPrime = true;
for(int i = 0; i < count; i++){
if(i % a[i] == 0){
isPrime = false;break;
}
}
if(isPrime){
if(count == a.getSize())
a.resize(count * 2);
a[count++] = i;
}
}
for(int i = 0; i < count; i++){
cout << setw(8) << a[i];
}
cout << endl;
return 0;
}
48、泛型程序设计
//编写不依赖于具体数据类型的程序
//将算法从特定的数据结构中抽象出来,成为通用的
//C++ 的模板为泛型程序设计奠定了关键的基础
//概念:
// 用来界定具备一定功能的数据类型,例如
// 将“可以比大小的所有数据类型(有比较运算符)” 这一概念记为Comparable
// 将“具有公有的复制构造函数并可以用“=”赋值的数据类型”这一概念记为Assignable
// 将“可以比较大小、具有公有复制构造函数并可以用“=”赋值的所有数据类型”这个概念记作Sortable
// 对于两个不同的概念A和B,如果概念A所需求的所有功能也是概念B所需求的功能,那么就是说概念B是概念A的子概念。例如:
// Sortable既是Comparable的子概念,也是Assignable的子概念
//模型:
// 符合一个概念的数据类型称为该概念的模型,例如:
// int型是Comparable概念的模型
// 静态数组类型不是Assignable概念的模型(无法用“=”给这个静态数组赋值)
49、STL
//简介:
// 标准模板库(Standard Template Library, 简称STL)定义了一套概念体系,为泛型程序设计提供了逻辑基础
// STL中的各个类模板、函数模板的参数都是用这个体系中的概念来规定的
// 使用STL的模板时,类型参数既可以是C++标准库中已有的类型,也可以是自定义的类型——只要这些类型是所要求概念的模型
//STL基本组件:
// 容器(container)
// 容器适配器:stack(栈) queue(队列) priority_queue(优先队列)
// 迭代器——指向泛型的指针(iterator):
// 函数对象(function object):
// 一个行为类似函数的对象,对它可以像调用函数一样调用
// 函数对象是泛化的函数:任何普通的函数和任何重载了“()”运算符的类的对象都可以作为函数对象使用
// 使用STL的函数对象,需要包含头文件<functional>
// 算法(algorithms):
// 可以广泛用于不同对象和内置的数据类型
// STL包括70多个算法
// 例如:排序,消除,计数,比较,变换,置换和容器管理等
// 使用STL的算法,需要包含头文件<algorithm>
//迭代器是算法和容器的桥梁
// 将迭代器作为算法的参数、通过迭代器来访问容器而不是把容器直接作为算法的参数
// 将函数对象作为算法的参数而不是将函数所执行的运算作为算法的一部分
// 使用STL中提供的或自定义的迭代器和函数对象,配合STL的算法,可以组合出各种各样的功能
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <functional>
using namespace std;
int main(){
const int N = 5;
vector<int> s(N);
for(int i = 0; i < n; i++)
cin >> s[i];
transform(s.begin(),s.end(),ostream_iterator<int>(cout," "), negate<int>()); //取出相反数输出,输出间隔为 空格
cout << endl;
return 0;
}
//transform算法:
template <class InputIterator, class OutputIterator, class UnaryFunction>
OutputIterator transform(InputIterator first, InputIterator last, OutputIterator result, UnaryFunction op){
for(;first != last; ++first, ++result)
*result = op(*first);
return result;
}
//transform算法顺序遍历first和last两个迭代器所指向的元素;
//将每个元素的值作为函数对象op的参数;
//将op的返回值通过迭代器result顺序输出;
//遍历完成后result迭代器指向的是输出的最后一个元素的下一个位置,transform会将该迭代器返回
50、迭代器
//迭代器是算法和容器的桥梁
// 迭代器用作访问容器中的元素
// 算法不直接操作容器中的数据,而是通过迭代器间接操作
//算法和容器独立
// 增加新的算法,无需影响容器的实现
// 增加新的容器,原有的算法也能适用
//使用独立于STL容器的迭代器,需要包含头文件<iterator>;
//输入流迭代器和输出流迭代器:
// 输入流迭代器:
// istream_iterator<T>
// 以输入流(如cin)为参数构造
// 可用*(p++)获得下一个输入的元素
// 输出流迭代器:
// ostream_iterator<T>
// 构造时需要提供输出流(如cout)
// 可用(*p++) = x将x输出到输出流
// 二者均属于适配器:
// 适配器是用来为已有对象提供新的接口的对象
// 输入流适配器和输出流适配器为流对象提供了迭代器的接口
//迭代器支持的操作
// 迭代器是泛化的指针,提供了类似指针的操作(诸如++、*、->运算符)
// 提供了顺序访问容器中每个元素的方法;
// 输入迭代器
// 可以用来从序列中读取数据,如输入流迭代器
// 输出迭代器
// 允许向序列中写入数据,如输出流迭代器
// 前向迭代器
// 既是输入迭代器又是输出迭代器,并且可以对序列进行单向的遍历
// 双向迭代器
// 与前向迭代器相似,但是在两个方向上都可以对数据遍历
// 随机访问迭代器
// 也是双向迭代器,但是能够在序列中的任意两个位置之间进行跳转,如指针、使用vector的begin()、end()函数得到的迭代器
//迭代器的区间
// 两个迭代器表示一个区间:[p1, p2)
// STL算法常以迭代器的区间作为输入,传递输入数据
// 合法的区间
// p1经过n次(n > 0)自增(++)操作后满足p1 == p2
// 区间包含p1,但不包含p2
//Example
#include <algorithm>
#include <iterator>
#include <vector>
#include <iostream>
using namespace std;
//将来自输入迭代器的n个T类型的数值排序,将结果通过输出迭代器result输出
template <class T, class InputIterator, class OutputIterator>
void mySort(InputIterator first, InputIterator last, OutputIterator result){
//通过输入迭代器将输入数据存入向量容器s中
vector<T> s;
for(;first != last; ++first)
s.push_back(*first);
//对s进行排序,sort函数的参数必须是随机访问迭代器
sort(s.begin(), s.end());
copy(s.begin(), s.end(), result);//将s序列通过输出迭代器输出
}
int main(){
//将s数组的内容排序后输出
double a[5] = {1.2, 2.4, 0.8, 3.3, 3.2};
mySort<double>(a, a + 5, ostream_iterator<double>(cout," "));
cout << endl;//输出间隔为空格
return 0;
}
//迭代器的辅助函数
// advance(p, n)
// 对p执行n次自增操作
// distance(first, last)
// 计算两个迭代器first和last的距离,即对first执行了多少次“++”操作后能够使得first == last
51、容器
//容纳、包含一组元素的对象
//基本容器类模板
// 顺序容器:array(数组) vector(向量) deque(双端队列)forward_list(单链表) list(列表)
// 关联容器:srt(集合) multiset(多重集合) map(映射) multimap(多重映射)
// 无序关联容器:unordered_srt(无序集合) unordered_multiset(无序多重集合) unordered_map(无序映射) unorder_multimap(无序多重映射)
// 容器的通用功能
// 用默认构造函数构造空容器
// 支持关系运算符: == != < <= > >=
// begin(),end():获得容器首、尾迭代器
// clear():将容器清空
// empty():判断容器是否为空
// size():得到容器元素个数
// s1.swap(s2):将s1和s2量容器内容交换
//相关数据类型(S表示容器类型)
// S::iterator : 指向容器元素的迭代器类型
// S::const_iterator : 常迭代器类型
//对可逆容器的访问
// STL为每个可逆容器都提供了逆向迭代器,逆向迭代器可以通过下面的成员函数得到:
// rbegin():指向容器尾的逆向迭代器
// rend():指向容器首的逆向迭代器
// 你想迭代器的类型名的表示方式如下:
// S::reverse_iterator : 逆向迭代器类型
// S::const_reverse_iterator :逆向常迭代器类型
//随机访问容器
// 支持对容器的元素进行随机访问, s[n] : 获得容器s的第n个元素
1、顺序容器
//STL中的顺序容器 在逻辑上可看作是一个长度可扩展的数组
// 向量( vector )
// 双端队列( deque )
// 列表( list )
// 单向链表( forward_list )
// 数组( array )
//元素线性排列,可以随时在指定位置插入元素和删除元素
//必须符合Assignable这一概念(即具有共有的复制构造函数并可以用“=”赋值)
//array对象的大小固定,forward_list有特殊的添加和删除操作
//顺序容器的接口(不包含单向链表(forward_list)和数组(array))
// 构造函数
// 赋值函数
// assign
// 插入函数
// insert, push_front(只对list和deque), push_back, emplace, emplace_front
// 删除函数
// erase,clear,pop_front(只对list和deque),pop_back, emplace_back
// 首位元素直接访问
// front,back
// 改变大小
// resize
//Example
#include <iostream>
#include <list>
#include <deque>
template <class T>
void printContainer(const char* msg, const T& s){
cout << msg << ":";
copy(s.begin(), s.end(),ostream)itertor<int>(cout, " "));
cout << endl;
}
int main(){
//从标准输入读入10个整数,将他们分别从s的头部加入
deque<int> s;
for(int i = 0; i < 10; i++){
int x;
cin >> x;
s.push_front(x);
}
printContaniner("deque at first", s);
//用s容器的内容的逆序构成列表容器l
list<int> l(s.rbegin(), s.rend());
printContainer("list at first", l);
//将列表容器l的每相邻两个元素顺序颠倒
list<int>::iterator iter = l.begin();
while(iter != l.end()){
int v = *iter;
iter = l.erase(iter);
l.insert(++iter, v);
}
printContainer("list at last", l);
//用列表容器l的内容给s赋值,将s输出
s.assign(l.begin(), l.end());
printContainer("deque at last", s);
return 0;
}
//向量
// 特点:
// 一个可以扩展的动态数组
// 随机访问、在尾部插入或删除元素快
// 在头部或中间插入或删除元素慢
// 向量的容量:
// 容量(capacity):实际分配空间的大小
// s.capacity():返回当前容量
// s.reserve(n):若容量小于n,则对s进行扩展,使其容量至少为n
//双端队列
// 特点:
// 在两端插入或删除元素快
// 在中间插入或删除元素慢
// 随机访问较快,但比向量容器慢
int main(){
istream_iterator<int> i1(cin), i2;
vector<int> s1(i1, i2);
sort(s1.begin(), s1.end());
deque<int> s2;
//以下循环遍历s1
for(vector<int>::iterator iter = s1.begin(); iter != s1.end(); ++iter){
if(*iter % 2 == 0) //偶数放到s2尾部
s2.push_back(*iter);
else
s2.push_front(*iter);
}
copy(s2.begin(), s2.end(), ostream_iterator<int>(cout, " "));
cout << endl;
return 0;
}
//列表(list)
// 特点:
// 在任意位置插入和删除元素都很快
// 不支持随机访问
// 接合(splice)操作:
// s1.splice(p, s2, q1, q2) : 将s2中的[q1, q2)移动到s1中p所指向元素之前
int main(){
string names1[] = {"Alice", "Helen", "Lucy", "Susan"};
string names2[] = {"Bob", "David", "Levin", "Mike"};
//用names1数组的内容构造列表s1
list<string> s1(nams1, names1 + 4);
//用names2数组的内容构建列表s2
list<string> s2(nams2, names2+4);
//将s1的第一个元素放到s2的最后
s2.splice(s2.end(), s1, s1.begin());
list<string>::iterator iter1 = s1.begin();//iter1指向s1首
advance(iter1, 2); //iter1前进两个元素,他将指向s1第三个元素
list<strin>::iterator iter2 = s2.begin();//iter2指向s2首
++iter2; //iter2前进1个元素,他将指向s2第二个元素
list<string>::iterator iter3 = iter2; //iter2初始化iter3
advance(iter3, 2); //iter3前进2个元素,他将指向s2第4个元素
//将(iter2, iter3)范围内的节点接到s1中iter1指向的结点之前
s1.splice(iter1, s2, iter2, iter3);
//分别将s1和s2输出
copy(s1.begin(), s1.end(), ostream_iterator<string>(cout, " "));
cout << endl;
copy(s2.begin(), s2.end(), ostream_iterator<string>(cout, " "));
cout << endl;
return 0
}
//单向链表(forward_list)
// 单向链表每个结点只有指向西安一个结点的指针,没有简单的方法来获取一个结点的前驱;
// 未定义insert、emplace和erase操作,而定义了 insert_after、emplace_after和erase_after操作,其参数与list的insert、emplace和erase相同,但并不是插入或删除迭代器p1所指的元素,而是对p1所指元素之后的结点进行操作;
// 不支持size操作
//数组(array)
// array是对内置数组的封装,提供了更安全,更方便的使用数组的方式
// array的对象的大小是固定的,定义时除了需要指定元素类型,还需要指定容器大小
// 不能动态地改变容器大小
//顺序容器的插入迭代器
// 用于向容器头部、尾部或者中间指定位置插入元素的迭代器
// 包括前插迭代器(front_inserter)、后插迭代器(back_insrter)和任意位置插入迭代器(inserter)
//Example
list<int> s ;
back_inserter iter(s);
*(iter++) = 5;
//顺序容器的适配器
// 以顺序容器为基础构建一些常用数据结构,是对顺序容器的封装
// 栈(stack):最先压入的元素最后被弹出
// 队列(queue):最先压入的元素最先被弹出
// 优先级队列(priority_queue):最 大 的元素最先被弹出
//栈和队列模板
// 栈模板
template<class T, class Sequence = deque<T>> class stack;
// 队列模板
template<class T, class FrontInsertionSequence = deque<T>> class queue;
//栈和队列共同支持的操作
// s1 op s2 op可以时==、!=、<、<=、>、>=之一,它会对两个容器适配器之间的元素按字典序惊醒比较
// s.size() 返回s的元素个数
// s.empty() 返回s是否为空
// s.push(t) 将元素t压入到s中
// s.pop() 将一个元素从s中弹出,对于栈来说,每次弹出的是最后被压入的元素,而对于队列,每次被弹出的是最先被压入的元素
// 不支持迭代器,因为他们不允许对任意元素进行访问
//栈和队列不同的操作
// 栈的操作:
// s.top() 返回栈顶元素的引用
// 队列操作:
// s.front() 获得队头元素的引用
// s.back() 获得队尾元素的引用
int main(){
stack<char> s;
string str;
cin >> str; //从键盘输入一个字符串
//将字符串的每个元素顺序压入栈中
for(string::iterator iter = str.begin(); iter != str.end(); ++iter)
s.push(*iter);
//将栈中的元素顺序弹出并输出
while(!s.empty()){
cout << s.top();
s.pop();
}
cout << endl;
return 0;
}
//优先级队列
// 优先级队列也像栈和队列一样支持元素的压入和弹出,但元素弹出的顺序与元素的大小有关,每次弹出的总是容器中最"大"的一个元素
template<class T, class Sequence = vector<T>>class priority_queue;
// 优先级队列的基础容器必须是支持随机访问的顺序容器
// 支持栈和队列的size、empty、push、pop几个成员函数,用法与栈和队列相同
// 优先级队列并不支持比较操作
// 与栈类似,优先级队列提供一个top函数,可以获得下一个即将被弹出元素(即最大的元素)的引用
//Example 细胞分裂模拟
//模拟一种细胞在诞生(即上次分裂)后会在500到2000秒内分裂为两个细胞,每个细胞又按照相同的规律继续分裂
const int SPLIT_TIME_MIN = 500;
const int SPLIT_TIME_MAX = 2000;
class Cell;
priority_queue<Cell> cellQueue;
class Cell{ //细胞类
private:
static int count; //细胞总数
int id; //当前细胞编号
int time; //细胞分裂时间
public:
Cell(int birth):id(count++){
time = birth + (rand() % (SPLIT_TIME_MAX - SPLIT_TIME_MIN) + 500);
}
int getId() const{ return id; }
int getSplitTime() const { return time; }
bool operator < (const Cell& s)const {return time > s.time;}
void split(){
Cell child1(time), child2(time);
cout << time << "s:Cell #" << id << "splits to #" << child1.getId() << "and #" << child2.getId() << endl;
cellQueue.push(child1);
cellQueue.push(child2);
}
};
int Cell::count = 0;
int main(){
srand(static_cast<unsigned>(time(0)));
int t;
cout << "Simulation time: ";
ci >> t;
cellQueue.push(Cell(0));
while(cellQueue.top().getSplitTime() < t){
cellQueue.top().split();//模拟下一个细胞的分裂
cellQueue.pop(); //将刚刚分裂的细胞弹出
}
return 0;
}
2、关联容器
//关联容器的特点
// 每个关联容器都有一个键(key)
// 可以根据键高效地查找元素
//接口
// 插入:insert
// 删除:erase
// 查找:find
// 定界:lower_bound、upper_bound、equal_range
// 计数:count
//四种关联容器
// 单重关联容器(set 和 map)
// 键值是唯一的,一个键值只能对应一个元素
// 多重关联容器(multiset 和 multimap)
// 键值是不唯一的,一个键值可以对应多个元素
// 建档关联容器(set 和 multiset)
// 容器只有一个类型参数,如set<K>、multiset<K>, 表示键类型
// 容器的元素就是键本身
// 二元关联容器(map和multimap)
// 容器有两个类型参数,如map<K,V>、multimap<K,V>,分别表示键和附加数据的类型
// 容器的元素类型是pair<K, V>,即由键类型和元素类型复合而成的二元组
//集合(set)
// 集合用来存储一组无重复的元素。由于集合的元素本身是有序的,可以高效地查找指定元素,也可以方便地得到指定大小范围的元素在容器中所处的区间
//Example: 找到输入集合的中值,输出小于中值的元素
int main(){
set<double> s;
while(true){
double v;
cin >> v;
if(v == 0) break; //输入0表示结束
//尝试将v插入
pair<set<double>::iterator, bool> r = s.insert(v);
if(!r.second) //若v已存在,输出提示信息
cout << v << "is duplicated" << endl;
//pair类:pair是一种模板类型,其中包含两个数据值,两个数据的类型可以不同,基本的定义如下:
//pair<int, string> a;
//操作:a.first, a.second
//得到首 尾元素的迭代器
set<double>::iterator iter1 = s.begin();
set<double>::iterator iter2 = s.end();
double medium = (*iter1 + *(--iter2)) / 2;
cout << "<= medium:"
copy(s.begin(), s.upper_bound(medium), ostream_iterator<double>(cout, " "));
cout << endl;
return 0;
}
}
//映射
#include <iostream>
#include <map>
#include <cctype>
using namespace std;
int main(){
map<char, int> s; //用来存储字母出现次数的映射
char c; //存储输入字符
do{
cin >> c; //输入下一个字符
if(isalpha(c)){ //判断是否是字母
c = tolower(c); //将字母转换为小写
s[c]++; //将该字母的出现频率加1
}
}while(c != '.'); //碰到 “.” 则结束输入
//输出每个字母出现次数
for(map<char, int>::iterator iter = s.begin(); iter != s.end(); ++iter)
cout << iter->first << " " << iter->second << " ";
cout << endl;
return 0;
}
//多重集合(multiset)与多重映射(multimap)
//多重集合是允许有重复元素的集合,多重映射是允许一个键对应多个附加数据的映射
//多重集合与集合、多重映射与映射的用法差不多,只在几个成员函数上有细微差异,其差异主要表现在去除了键必须唯一的限制
//上课时间查询
int main(){
multimap<string, string> courses;
typedef multimap<string, string>::iterator CourseIter;
//将课程上课时间插入courses映射中
courses.insert(mak_pair("C++", "2-6"));
courses.insert(make_pair("COMPILER", "3-1"));
courses.insert(make_pair("COMPILER", "5-2"));
courses.insert(make_pair("OS", "1-2"));
courses.insert(make_pair("OS", "4-1"));
courses.insert(make_pair("OS", "5-5"));
//
string name;
int count;
do{
cin >> name;
count = courses.count(name);
if(count == 0)
cout << "Cannot find this course!" << endl;
}while(count == 0);
cout << count << " lesson(s) per week: ";
pair<CourseIter, CourseIter> range = courses.equal_range(name);
for(CourseIter iter = range.first; iter != range.second; ++iter)
cout << iter->second << " ";
cout << endl;
return 0;
}
52、函数对象
函数对象
一个行为类似函数的对象
可以没有参数,也可以带有若干参数
其功能是获取一个值,或者改变操作的状态
例子:
普通函数就是函数对象
重载了“()”运算符的类的实例就是函数对象
#include <iostream>
#include <numeric> //包含数值算法头文件
using namespace std;
//定义一个普通函数
int mult(int x, int y){ return x * y }
int main(){
int a[] = {1, 2, 3, 4, 5};
const int N = sizeof(a) / sizeof(int);
cout << "The result by multipling all elements in a is "<< accumulate(a, a+N, 1, mult) << endl;
return 0;
}
#include <iostream>
#include <numeric>
using namespace std;
class MultClass{
public:
int operator()(int x, int y) const {return x * y;}
};
int main(){
int a[] = {1, 2, 3, 4, 5};
const int N = sizeof(a) / sizeof(int);
cout << "The result by multipling all elements in a is "<< accumulate(a, a+N, 1, MultClass()) << endl;
return 0;
}
STL提供的函数对象:
用于算术运算的函数对象:
一元函数对象(一个参数):negate (否定)
二元函数对象(两个参数):plus、minus、multiplies、divides、modulus
用于关系运算、逻辑运算的函数对象(要求返回值为bool)
一元谓词(一个参数):logical_not
二元谓词(两个参数):equal_to、not_equal_to、greater、less、greater_equal、less_equal、logical_and、logical_or
accumulate(a, a+N, 1, multiplies<int>())
53、函数适配器
绑定适配器:bind1st、bind2nd
将n元函数对象的指定参数绑定为一个常数,得到n-1元函数对象
组合适配器:not1、not2
将指定谓词的结构取反
函数指针适配器:ptr_fun
将一般函数指针转换为函数对象,使之能够作为其他函数适配器的输入
在进行参数绑定或其他转换的时候,通常需要函数对象的类型信息,例如bind1st和bind2nd要求函数对象必须继承于binary_function类型。但如果传入的是函数指针形式的函数对象,则无法获得函数对象的类型信息
成员函数适配器:ptr_fun、ptr_fun_ref
对成员函数指针使用,把n元成员函数适配为n+1元函数对象,该函数对象的第一个参数为调用该成员函数时的目的对象
也就是需要将”object->method()“转换为”method(object)"形式。将“object->method(arg1)”转为二元函数“method(object, arg1)”
#include <functional>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
int intArr[30, 90, 10, 40, 70, 50, 20, 80];
const int N = sizeof(intArr) / sizeof(int);
vector<int> a(intArr, intArr+N);
vector<int>::iterator p = find_if(a,begin(), a.end(), bind2nd(greater<int>(), 40));
if(p == a.end())
cout << "no element greater than 40" << endl;
else
cout << "first element greater than 40 is:" << *p << endl;
return 0;
//find_if算法在STL中的原型声明为:
//template<class InputIterator, class UnaryPredicate>
//InputIterator find_if(InputIterator first, InputIterator last, UnatyPredicate pred);
//它的功能时查找数组[first,last)区间中第一个pred(x)为真的元素
//pred需要时一元谓词
}
//ptr_fun not1 not2产生函数适配器实例
#include <functional>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool g(int x, int y){
return x > y;
}
int main(){
int intArr[30, 90, 10, 40, 70, 50, 20, 80};
const int N = sizeof(intArr) / sizeof(int);
vector<int> a(intArr, intArr+N);
vector<int>::iterator p;
p = find_if(a.begin(), a.end(), bind2nd(ptr_fun(g), 40));
if(p == a,end())
cout << "no element greater than 40" << endl;
else
cout << "first element greater than 40 is:" << *p << endl;
p = find_if(a.begin(), a.end(), not1(bind2nd(greater<int>(), 15)));
if(p == a.end())
cout << "no element is not greater than 15" << endl;
else
cout << "first element that is not greater than 15 is:" << *p << endl;
return 0;
}
//成员函数适配器实例
#include <functional>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Car{
int id;
Car(int id){ this->id = id; }
void display() const { cout << "car" << id << endl; }
};
int main(){
vector<Car *>pcars;
vector<Car> cars;
for(int i = 0; i < 5; i++)
pcars.push_back(new Car(i));
for(int i = 5; i < 10; i++)
cars.push_back(Car(i));
cout << "elements in pcars:" << endl;
for_each(pcars.begin(), pcars.end(), std::mem_fun(&Car::display));
//将范围内的元素使用第三个参数的功能遍历每个元素, 将成员函数适配成普通函数
cout << endl;
cout << "elements in cars: " << endl;
for_each(cars.begin(), cars.end(), std::mem_fun_ref(&Car::display));
//用mem_fun适配出来的函数的参数是对象的指针,mem_fun_ref适配出来的函数的参数是对象的引用
cout << endl;
for(size_t i = 0; i < pcars.sie(); ++i)
delete pcars[i];
return 0;
}
54、STL算法
STL算法本身是一种函数模板
通过迭代器获得输入数据
通过函数对象对数据进行处理
通过迭代器将结果输出
STL算法是通用的,独立于具体的数据类型、容器类型
STL算法分类
不可变序列算法
不直接修改所操纵的容器内容的算法
用于查找指定元素、比较两个序列是否相等、对元素进行计数等
例:
template<class InputIterator, class UnaryPredicate>
InputIterator find_if(InputIterator first, InputIterator last, UnaryPredicate pred);
查找[first, last)区间内pred(x)为真的首个元素
可变序列算法
可以修改他们所操作的容器对象
包括对序列进行复制、删除、替代、倒序、旋转、交换、分割、去重、填充、洗牌的算法及生产一个序列的算法
例子:
template<class ForwardIterator, class T>
InputIterator find_if(ForwardIterator first, ForwardIterator last, const T& x);
将[first, last)区间内的元素全部改写为x
排序和搜索算法
对序列进行排序
对两有序序列进行合并
对有序序列进行搜索
有序序列的集合操作
堆算法
例如:
template<class RandomAccessIterator, class UnaryPredicate>
void sort(RandomAccessIterator first, RandomAccessIterator last, UnaryPredicate comp);
以函数对象comp为“<”,对[first, last)区间内的数据进行排序
数值算法
求序列中元素的“和”、部分“和”、相邻元素的“差”或两序列的内积
求“和”的“+”、求“差”的“-”以及求内积的“+”和“*”都可由函数对象指定
例如:
template<class InputIterator, class OutputIterator, class BinaryFunction>
OutputIterator parital_sum(InputIterator first, InputIterator last, OutputIterator result, BinaryFunction op);
对[first, last)内的元素求部分“和”(所谓部分和,是一个长度与输入序列相同的序列,其第n项为输入序列前n个元素的和),以函数对象op为“+”运算符,结果通过result输出,返回的迭代器指向输出序列最后一个元素的下一个元素
55、IO流的概念及流类库的结构
流是信息流动的一种抽象,它负责再数据的生产者和数据的消费者之间建立联系,并管理数据的流动
流对象与文件操作
程序建立一个流对象
指定这个流对象与某个文件对象建立连接
程序操作流对象
流对象通过文件系统对所连接的文件对象产生作用
提取与插入
读操作在流数据抽象中被称为(从流中)提取
写操作被称为(向流中)插入
输出流概述
最重要的三个输出流
ostream
ofstream
ostringstream
预先定义的输出流对象
cout 标准输出
cerr 标准错误输出,没有缓冲,发送给它的内容立即被输出
clog 类似于cerr,但是又缓冲,缓冲区满时被输出
标准输出换向
ofstream fout("b.out");
streambuf* pOld = cout.rdbuf(fout.rdbuf());
//...
cout.rdbuf(pOld);
构造输出流对象
ofstream类支持磁盘文件输出
如果在构造函数中指定一个文件名,当构造这个文件时该文件时自动打开的
ofstream myFile("filename");
可以在调用默认构造函数之后使用open成员函数打开文件
ofstream myFile;//声明一个静态文件输出流对象
myFile.open("filename");//打开文件,使流对象与文件建立联系
在构造对象或用open打开文件时可以指定模式
ofstream myFile("filename", ios_base::out | ios_base::binary); //|同时使用,binary二进制文件,out用于输出
文件输出流成员函数的三种类型
与操纵符等价的成员函数
执行非格式化写操作的成员函数
其他修改流状态且不同于操纵符或插入运算符的成员函数
文件输出流成员函数
open函数
把流与一个特定的磁盘文件关联起来
需要指定打开模式
put函数
把一个字符写到输出流中
write函数//二进制写函数
将内存中的一块内容(以原样的二进制)写到一个文件输出流中
seekp和tellp函数
操作文件流的内部指针
close函数
关闭与一个文件输出流关联的磁盘文件
错误处理函数
在写道一个流时进行错误处理
向文本文件输出
插入(<<)运算符
为所有标准C++数据类型预先设计的。用于传送字节到一个输出流对象
操纵符(manipulator)
插入运算符与操纵符一起工作
控制输出格式
很多操作符都定义在
ios_base类中(如hex())、<iomanip>头文件(如setprecision())
控制输出宽度
在流中放入setw操纵符或调用width成员函数为每个项指定输出宽度
setw和width仅影响紧随其后的输出项,但其它流格式操纵符保持有效直到发生改变
dec、oct和hex操纵符设置输入和输出的默认进制
#include <iostream>
using namespace std;
int main(){
double values[] = {1.23, 35.36, 653.7, 4358.24};
for(int i = 0; i < 4; i++){
cout.width(10); //设置填充字符setfill(x);
cout << setfill('0') << values[i] << endl;
}
return 0;
}
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
int mian(){
double values[] = {1.23, 35.36, 653.7, 4358.24};
string names[] = {"Zoot", "Jimmy", "AI", "Stan"};
for(int i = 0; i < 4; i++)
cout << setiosflags(ios_base::left)//左对齐
<< setw(6) << names[i]
<< resetiosflags(ios_base::left)//取消
<<setw(10) << values[i] << ednl;
return 0;
}
setiosflags操纵符
这个程序中,通过使用带参数的setiosflags操纵符来设置左对齐,ertiosflags定义的头文件iomanip中
赞数ios_base::left是ios_base的静态常量,因此引用时必须包括ios_base::前缀
这里需要用resetiosflags操纵符关闭左对齐标志,setiosflags不同于width和setw,它的影响是持久的,知道使用resetiosflags重新恢复默认值时为止
setiosflags的参数时该流的格式标志值,可用按位或(|)运算符进行组合
精度:
如果不指定fixed或scientific,精度值表示有效数字位数
如果设置了ios_base::fixed或ios_base::scientific精度值表示小数点之后的位数
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
int main(){
double values[] = {1.23, 35.36, 653.7, 4358.24};
string names[] = {"Zoot", "Jimmy", "AI", "Stan"};
cout << setiosflags(ios_base::fixed);//定点表示,1为小数点后的位数
cout << setiosflags(ios_base::scientific);//科学计数法表示,小数点后的位数为1位
for(int i = 0; i < 4; i++)
cout << setiosflags(ios_base::left)
<< setw(6) << names[i]
<< resetiosflags(ios_base::left)
<< setw(10) << setprecision(1)
<< values[i] << endl;
return 0;
}
向二进制文件输出
二进制文件流
使用ofstream构造函数中的模式参量指定二进制输出模式
以头长方式构造一个流,然后使用setmode成员函数,在文件打开后改变模式
通过二进制文件输出流对象完成输出
#include <fstream>
using namespace std;
struct Date{
int mon,day,year;
};
int main(){
Date dt = {6, 10, 92};
ofstream file("date.dat", ios_base::binary);
file.write(reinterpret_cast<char *>(&dt), sizeof(dt));
file.close();
return 0;
}
向字符串输出
字符串输出流( ostringstream )
用于构造字符串
功能
支持ofstream类的除open、close外的所有操作
str函数可以返回当前依构造的字符串
典型应用
将数值转换为字符串
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
template <class T>
inline string toString(const T &v){
ostringstream os;
os << v;
return os.str();
}
int main(){
string str1 = toString(5);
cout << str1 << endl;
string str2 = toString(1.2);
cout << str2 << endl;
return 0;
}
输入流概述
重要的输入流类:
istream类最适合用于顺序文本模式输入,cin是其实例
ifstream类支持磁盘文件输入
istringsteam
构造输入流对象
如果在构造函数中指定一个文件名,在构造该对象时该文件便自动打开
ifstream myFile("filename");
在调用默认构造函数之后使用open函数来打开文件
ifstream myFile;//建立一个文件流对象
myFile.open("filename");//打开文件"filename"
打开文件时可以指定模式
ifstream myFile("filename", ios_base::in | ios_base::binary);
使用提运算符从文本文件输入
提取运算符(>>)对于所有标准C++数据类型都是预先设计好的
时从一个输入流对象获取字节最容易的方法
ios类中的许多操纵符都可以应用于输入流,但是只有少数几个对输入流对象具有实际影响,其中最重要的时进制操纵符dec、oct和hex
输入流相关函数
open函数把该流与一个特定磁盘文件相关联
get函数的功能与提取运算符(>>)很相像,主要`在这里插入代码片`的不同点时get函数在读入数据时包括空白字符
#include <iostream>
using namespace std;
int main(){
char ch;
while((ch = cin.get()) != EOF)
cout.put(ch);
return 0;
}
getline的功能时从输入流中读取多个字符,并且允许指定输入终止字符,读取完成后,从读取内容中删除终止字符
#include <iostream>
#include <string>
using namespace std;
int main(){
ifstream in("Data.txt");
string line;
const int bsz = 1024;
char buf[bsz];
in.getline(buf, bsz);//将in绑定的文件按行输入进buf里,一次最多输入bsz个字符
getline(cin, line, 't');//文本文件输入流对象 string对象 分隔符
cout << line << endl;
return 0;
}
read成员函数从一个文件读字节到一个指定的内存区域,由长度参数确定要读的字节数。当遇到文件结束或者在文本模式文件中遇到文件结束标志字符时结束读取
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;
struct SaaryInfo{
unsigned id;
double salary
};
int main(){
SalaryInfo employee1 = {600001, 8000};
ofstrem os("payroll", ios_base::out | ios_base::binary);
os.write(reinterpret_cast<char *>(&employee1), sizeof(employee1));
os.close();
ifstream is("payroll", ios_base::in | ios_base::binary);
if(is){
SalaryInfo employee2;
is.read(reinterpret_cast<char *>(&employee2), sizeof(employee2));//从文件开头读取employee2大小的数据存放到employee2中
cout << employe2.id << " " << employee2.salary << endl;
}
else
cout << "ERROR:Cannot open file'playroll'."<< endl;
is.close();
return 0;
}
seekg函数用来设置文件输入流中读取数据位置的指针
int main(){
int values[] = {3, 7, 0, 5, 4};
ofstream os("integers", ios_base::out | ios_base::binary);
os.write(reinterpret_cast<char *>(values), sizeof(values));
os,close();
ifstream is("integers", ios_base::in | ios_base::binary);
if(is){
is.seekg(3*sizeof(int));//移动字节数
int v;
is.read(reinterpret_cast<char *>(&v), sizeof(int));
cout << "The 4th integer in the file 'intergers' is " << v << endl;
}
else{
cout << "ERROR:Cannot open file'playroll'."<< endl;
}
return 0;
}
tellg函数返回当前文件读指针的位置
int main(){
ifstream file("integers", ios_base::in | ios_base::binary);
if(file){
while(file){//读到文件尾file为0
streampos here = fill.tellg();
int v;
file.read(reinterpet_cast<char *>(&v), sizeof(int));
if(file && v == 0)
cout << "Postion" << here << "is 0" << endl;
}
}
else
cout << "ERROR:Cannot open file'playroll'."<< endl;
file.close();
return 0;
}
close函数关闭与一个文件输入流关联的磁盘文件
字符串输入流( istringstream )
用于从字符串读取数据
在构造函数中设置要读取的字符串
功能
支持ifstream类的除open、close外的所有操作
典型引用
将字符串转换为数值
template<class T>
inline T fromString(const string &str){
istringstream is(str);
T v;
is >> v;
return v;
}
int main(){
int v1 = fromSting<int>("5");
cout << v1 << endl;
doule v2 = fromSting<double>("1.2")
cout << v2 << endl;
return 0;
}
两个重要的输入/输出流
一个iostream对象可以是数据的源或目的
两个重要的I/O流类都是从iostream派生的,他们是fsteam和stringstream,这些类继承了前面描述的istream和ostream类的功能
fstream类
fstream类支持磁盘文件输入和输出
如果需要在同一个程序中从一个特定磁盘文件读并写到该磁盘文件,可以构造一个fstream对象
一个fstream对象是由两个逻辑子流的单个流,两个子流用于输入,另一个用于输出
stringstream类
stringstream类支持面向字符串的输入和输出
可以用于对同一个字符串的内容交替读写,同样是由两个逻辑子流构成