记录cpp中大部分的知识点,个人理解有限,不一定都正确。
1
2
3
4
5
6
7
|
cout<<setw(8)<<a<<endl; //setw()在头文件<iomanip>中,只能右对齐,等价%8d
cout<<setiosflags(ios::left)<<setw(8)<<a<<endl;//这样为左对齐
cout<<setw(8)<<setprecision(2)<<setiosflags(ios::fixed)<<b<<endl;//b是一个浮点数,等价于%8.2f
cout<<setfill('x')<<set(8)<<a<<endl;//将空白用x填充
cout<<hex<<a<<endl;//等价于printf("%x\n",a);十六进制
cout<<oct<<a<<endl;//等价于printf("%o\n",a);八进制
cout<<a<<endl;//等价于printf("%d\n",a);十进制
|
匹配原则
- 严格匹配
- 隐式转换(小到大可以,大到小不可以)寻求匹配,找到则匹配
- 返回值不构成重载
1
2
3
4
5
6
7
8
9
10
11
12
|
extern "C"{} //将括号内按c的方式编译,不与其他同名函数重载。c库每一个头文件都有
//---------------------------------------------------------
#ifdef __cplusplus//如果是c++编译器
extern "c"{
#endif // __cplusplus
// 内容
#ifdef __cplusplus
}
#endif //表示不使用c++的重载特性
//----------------------------------------------------------
|
1
2
3
4
5
6
7
8
9
10
11
|
//运算符其实就是函数
struct zuobiao //坐标
{
int x;int y;
};
zuobiao operator + (zuobiao a,zuobiao b)
{
zuobiao c;
c.x=a.x+b.x; c.y=a.y+b.y;
return c;
}
|
参数只能从右往左默认,中间不能跳跃
&
前为类型名时为引用,其他均为取地址
变量名,本身是一段内存的引用,即别名(alias)。此处引入的引用是为已有变量起一个别名,声明如下
1
2
3
4
5
6
7
8
9
10
|
int a;
int &ra=a;
//1. 引用是一种声明关系,必须初始化,引用不开辟空间
//2. 此种声明关系一经声明 不可变更
int &rr=ra;
//3. 可以对引用,再次引用。多次引用的结果,多个引用指向同一个变量
//引用的本质是对指针的包装,指针可以有引用,但不应该有引用的指针
//const int&ra=a; 能用const就用;use const whatever possible
//思考:引用的本质是什么? int & ra = a; int * const p = &a; 引用的本质是不可以修改指向的指针;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
int *p = new int(200); //开一个int类型单变量并初始化为200
int *ps = new string("hellwo world");//开一个string类型单变量并初始化为 hellow world
int *p1 = new int[5]{0};//开一个int数组 每一项初始化为0;
int **pp = new int*[5];//指针数组,存放的指针
int (*p)[4] = new int[3][4] {{0}};//p是一个指针 它指向包含4个int元素的一维数组 p为首地址 记不住怎么写编译器可以给你提示
//也可以new一个结构体 但初始化应用 {} 因为可能有有多个值
/*应用程序在内存中分为
代码区、
全局(静态)数据存储区、
栈区、
堆区。
通过new出来的内存在堆区。
局部变量,函数参数等存在栈区
*/
int *p = new int ;
delete p;
int *p=new int[5];
delete []p;
int (*p)[4][3]=new int[5][4][3];
delete []p;
//异常处理 new很少出错 一般别用
int *p = new(std::nothrow) int ;
if(p == nullptr){return -1}//NULL 好像变成nullptr了c++11
|
1
2
3
4
5
|
inline int sqr(int i)
{
return i*i;
}//inline 是给编译器的一种建议,编译器来决定是否inline,但你不说编译器一定不inline 66666
//适用环境 函数体很小 且 频繁使用
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
int a=5; float b=6.5;
static_cast //对于隐式类型可以转化的,可用这个
b=static_cast<float>(a); //作者不想让你强转,所以搞这么麻烦 哈啊哈
void *p;int *q;
p=q;//可以
q=p;//不可以 q=static_cast<int*>(p);OK
dynamic_cast
reinterpret_cast //对于无隐式类型的转化,static_cast无效,例如将int赋值给int*指针 reinterpert重新解释
const_cast //脱常 只能用于指针和引用
void fun1(int & v)
{
cout<<v<<endl;
}
int main()
{
const int a;
fun1(a); //这是传不进去的
fun1(const_cast<int &>(a));//这样就传进去了 不要妄图用这个方法修改const 其行为是未定义的且没有意义
return 0;
}
|
1
2
3
|
#define N 200 //宏,在预处理阶段发生了替换
const int n = 200; //在编译阶段发生了替换
//所以const永远不会发生改变
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
int v=55;
main()
{
int v=5;
cout<<v<<endl;//打印局部变量
cout<<::v<<endl;//打印全局变量
}//全局命名空间其实是一个没有名字的命名空间,也可以用::作用域运算符访问。
namespace //是对全局命名空间的再次划分
namespace name//协同开发 可以在不同文件中定义同名空间,将自动合并
{
int x;
void func()
{
printf("haha");
}
struct xy
{
int x;int y;
};
//可以写很多,包括再写一个命名空间
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//位于命名空间std;它是一个类不是关键字
//用cin输入时有空格会结束,但输出时字符串里可以有空格
string str="china",s2="0";
cout<<str.size()<<enld; //获得长度
char buf[100];
strcpy(buf,str.c_str());//strcpy位于string.h str.c_str()对象函数返回一个c语言类型的字符串,以兼容c
cout<<buf<<endl;
str.swap(s2);//用对象方法交换
int n=str.find('a',0);//查找a,返回下标,第二个参数为从0开始,找不到返回-1
int n=str.find("in",0);//查找字串,有返回首下标,没有返回-(
//删空格
string ss=" sdfsdfs ";
ss.erase(0,ss.find_first_not_of(' '));//从0删到第一个不是空格,参数一从什么地方删,参数二删几个
ss.erase(ss.find_last_not_of(' ')+1);//从最后一个不是空格删到最后
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
//c语言的封装风格,数据放在一起打包为struck,然后吧数据以引用或指针的方式传递给行为。
struct date
{
int year;
int month;
int day;
};
void init(date & d)
{
cin>>d.year;
cin>>d.month;
cin>>d.day;
}
//c++认为c封装不彻底,1数据和行为分离,2没有权限控制。
//权限控制 private protected public
class date2
{
private:
int year;
int month;
int day;
public:
viod print()
{
cout<<year<<endl;
}
};
//类名也是一种特别的名称空间
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//constructor 构造器
//1.与类名相同,无返回值,生成对象时自动调用,用于初始化
//2.可以有参数,可以重载,默认参数
class sss
{
public:
sss()
{
cout<<"我是构造器"<<endl;
}
sss(int i)
{
cout<<i;
}
};
sss s(100);//使用构造器的重载, 创建对象时加参数
|
1
2
3
4
5
6
7
8
9
10
|
//1.~类名()无参无反,对象销毁时的内存处理工作
class sss
{
public:
~ksss()
{
cout<<"我是析构器"<<endl;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
//代码in坚果云
class A
{
public:
A(int i=0;int j=0;int k=0)//参数列表,效率高,在函数声明之后,实现体之前,:开头
:x(i),y(j),z(k)//初始化顺序,跟变量的声明顺序有关,初始化列表中的顺序无关,即xyz顺序可变
{
}
private:
int x,y,z;
};
//拷贝构造器
//由已存在的对象,创建新对象,也就是所新对象不是由构造器来构造,而是由
//拷贝构造器来完成,拷贝构造器的格式是固定的
class A
{
A(const A & another)//&是引用
};
//若不提供,系统提供默认,自定义是系统不再提供默认
//系统提供的默认是一种等位拷贝,浅浅的拷贝,会导致内存重析构。doble free
//含有堆空间时要自实现拷贝
//友元 同类对象无私处,异类对象有友元
//运算符重载
//赋值运算符重载
string s4=s3;//本质不是赋值,而是拷贝构造,在声明时候=号表示初始化
//this指针
//系统创建对象时,默认生成当前对象的this指针。
//对对象来说,传引用效率很高,不然调用拷贝构造效率低。
//栈上的对象可以返回,但不能返回栈上的引用(除非实例不会销毁,比如对象方法返回自己,结束后对象还在,引用有效)
A & func(){//这是错的,func执行完b会销毁,但这个回传的引用仍指向这个地址
A b;
return b;
}
|
static
- 在c中,修饰全部变量表示吧作用于限制在本文件内
- 修饰局部变量表示吧变量生命周期提升为全局
- c++中在类内部,用来实现族类对象间的数据共享。
- c++中,修饰类成员时,表示这个成员变量属于整个类,而不是某个对象(既属于类,也属于对象,但终归于类)
1
2
|
static int a;//声明写在类内
int A::a=0;//初始化写在内外
|
- 修饰成员函数时,也属于整个类;因为他属于类,没有this指针(this只有对象实例有)
1
2
3
4
|
//不能访问数据成员及成员,没有this
static string getstr();
const static int h=0;
//static const 修饰数据成员,必须在类内初始化;就地初始化
|
const
- 修饰数据成员,初始化位置只能在 参数列表里;(新版本虽然可以直接等于号初始化,但别被惯傻了)
- 修饰成员函数(注意不是修饰返回类型),位置放在声明之后,实现体之前,大概也没有别的地方可以放了(haha)
- const函数承诺不会修改数据成员,也只能调用其他const函数。
- 还可以构成重载(wtf?)
- 修饰类对象,从对象层面不修改成员,只能调用const成员函数,构成重载就是配饰有无const的对象的情况
- 即const对象只能调用const成员函数,非const对象优先调用非const成员函数(若无则调用const成员函数)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//都指向非静态成员
"成员类型 类名::* 指针名 = &类名::成员名"
class stuu
{
void print();
private:
int a;
};
//指向数据成员
//在定义时必须和类关联,使用时必须和对象关联
int stuu::* pa=&stuu::a;
stuu dx;
cout<<dx.*pa<<endl;
//(内部实现,实际上时记录了偏移量)
//指向函数成员
//
void (stuu::*pf)()=&stuu::print;
(dx.*pf)();
tip://在类中想用常量时
enum{cnt=4};//因为用const必须参数列表初始化,static const 太浪费
|
- 友元可以是友元函数,友元类,异类才有友元(同类无私处)
- 友元是单方面的,你拿我当朋友不等于我也拿你当朋友(涩会涩会)
- 访问时必须通过
1
2
3
4
5
6
7
8
9
|
class A
{
friend void dis(A & t);
friend class B;
};
// 该函数可以访问A的私有成员
void dis(A & t);//访问时必须通过对象访问,因为他不是成员函数
// 该类可以访问A的私有成员
class B{void dis(A & t);};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//不能重载的运算符
. .* :: ?: sizeof //sizeof是运算符,不是函数
//除了=号外,基类中被重载的操作符都将被派生类继承
//“赋值运算符重载函数”不是不能被派生类继承,而是被派生类的默认“赋值运算符重载函数”给shadow了
//通常情况下,双目运算符重载为成员需要一个参数,重载为友元要两个参数,做成员时this也是参数
//重载-(负号)时用const修饰成员函数,-c=t就不会过编译了,这才是对的,注意-(-c)所以只能用const修饰成员函数而不是返回值。
//
Clock operator++(Clock& c) //前置单目运算符 ++c
Clock operator++(Clock& c,int) //后置单目运算符 c++
//day 6.2
//流输入输出运算符重载
istream & operator>>(istream&,自定义类&);
ostream & operator<<(istream&,const 自定义类&);
|
- 使用 类型转化构造器
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//实现 其他类型 到 本类型 的转化
class 本类型
{
本类型(const 其他类型 & Ta) //这是一个构造器
[
//实现转化
//this->data = Ta.data ;
]
};
本类型 = 其他类型; //隐式转化可用 先调用类型转化,在调用赋值运算符重载
//我们把只有一个参数的构造器成为类型转化构造器
explicit //加在转化构造器前表示只能使用 显式转化
本类型 = static_cast<本类型>(其他类型);
|
- 使用 类型转换操作符函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class 源类
{
//无参无返回
operator 目标类型(void)
{
//根据需要进行转化
//目类标型.data = this->data;
return 目标类型(this->data2,this.data2);//特殊,里面有返回,
//实际也可以这么写
目标类型 swa;
swa.data1 = this->data1;
return swa;
}
};
|
- 函数操作符()重载 – 仿函数
1
2
3
4
5
6
7
8
9
10
11
12
|
//把类对象当作函数名使用,主要应与于STL和模板
class 类名
{
返回值类型 operator() (参数类型)
{
}
};
//lambda 本质就是仿函数
operator bool();//对象可以对void * 重载,if(A)情况,会调用bool();
operator void *();//对象可以对void * 重载,if(A)情况,没有bool()会调用void*(),然后void*被强转成bool;
|
- 重载
new delete new[] delete[]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//定制化时使用,给特定类定制
void * operator new(size_t)
void operator delete(void *)
void * operator new[](size_t)
void operator delete[](void *)
//注:operator new 中 size_t 参数是编译器自动计算传递的 其实是 unsigned int
//了解一下就行↓
#include <memory>
class A{int a;};
auto_ptr<A> ptr(new A); //auto_ptr 类模板 auto_ptr<A>模板类
ptr->a=1;
//一个对象的行为想指针,重载了 -> () *
//new被ptr托管后,不需要再关心delete的问题
|
- c++ 通过继承关系实现了代码的可重用性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
class 派生类名:继承方式 基类名
{
};
//一个派生类可以有多个基类
//继承方式 public protected private
/*
父类 公有继承 保护继承 私有继承
成员属性↘ public public protected private
protected protected protected private
private 不可访问 不可访问 不可访问(inaccessable)
*/
protected 对于外界访问属性来说等同于 private,但在派生类中可见
几点说明:
* 可见不可见都全盘接收,除了构造器与析构器,基类可能会造成派生类的成员冗余,是需要设计的
* 派生类有了自己的个性,才有意义
|
- 派生类的构造器
1
2
3
4
5
6
7
|
类名(参数总列表)
:父类名(父类构造器列表),自己的成员
{
//在参数列表里调用父类构造器,然后再初始化自己的成员
//在父类中如果有标配,重载或默认,把无参的情况包含进来,子类可以不显式的调用父类构造器,隐式调用
}
//初始化顺序,父类,其他类对象成员,本类的初始化
|
- 派生类的拷贝构造器
1
2
3
4
5
6
7
8
|
//子类未实现拷贝时,会调用父类构造器
//子类一旦实现拷贝构造,则必须显式的调用父类拷贝构造器
son(const son & another)
:father(another)
{
// ↑↑↑赋值兼容(子类对象(引用或指针)可以赋给父类对象)
this->data=another.data;
}
|
- 派生类赋值重载
1
2
3
4
5
6
7
8
|
//子类中未实现赋值重载时,会调用父类的赋值重载
//子类一旦实现赋值重载,不会自动调用父类赋值重载
son& operator=(son& another)
{
//赋值兼容
father::operator=(another);//重载是会被子类继承的,所以可以调用。与构造器不同
this->data=another.data;
}
|
- 重名问题
- 子类中会把父类中重名的成员shadow掉,只需要名字相同即可
- 尽量不要重名,否则,调用时加上命名空间
- overload 重载 发生在同一作用域,函数名相同,参数列表不同(个数,类型,顺序)
- shadow 发生在父子类当中,只要函数名相同即可
- 派生类的友元函数
- 父类的友元函数,子类也可以调用
- 传参时子类赋值给了父类,赋值兼容
- 派生类的析构器
- 析构顺序:子类,成员,基类
- 与构造相反
- 多继承
- 你有n个爹(逃)
- 三角问题,多个父类中相同的成员,用到要加父类作用域
- 给调用者带来很大的不便
- 办法,提取相同成员到一个公共类M中,然后让被提取的类,分别继承这个公共类M(三角变成了四角)
- 但是子类还是无法访问这个成员,因为两个基类都继承了M,这个成员还是有两份
- 最终办法 虚继承
- 虚继承 virtual(加在继承方式前面)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//上面的 M 就叫做 虚基类
//虚继承的意义:在多继承中,保存各个父类的多份同名成员,虽然有时是必要的,可以在不同数据成员中存放不同数据,但在大多数情况下
//是我们不希望出现的,因为保留多份数据成员的拷贝,不仅占用较多的存储空间,还增加了访问困难
//虚基类和虚继承,实现了在多继承中只保留一份共同(同名)成员。
//虚基类,需要抽象和设计,虚继承,是一种继承的扩展
//初始化问题,多继承的类中一定要调用虚基类的构造器,父类可以用默认
class A{
A(int i){}
};
class B:virtual public A{
B(int n):A(n){}
};
class C:virtual public A{
C(int n):A(n){}
};
class D:public B,public C{
D(int n):A(n),B(n),C(n){}//结果有A()控制,其他配角
};
|
男人不止一面(逃),几个相似但不完全相同的对象,收到同一个消息后,执行不同的操作
c++中所谓的多态是指,由继承而产生的相关的不同的类,其对象对同一消息会做出不同的响应
增加程序的灵活性,可以减轻系统升级,维护,调试的工作量和复杂度
- 赋值兼容(多态实现的前提)
- 赋值兼容规则 是指在需要基类对象的任何地方都可以使用 公有派生类(其他不行) 的对象来替代,赋值兼容是一种默认行为,不需要任何的显式步骤
- 派生类的对象可以赋值给基类对象
- 派生类的对象可以初始化基类的引用
- 派生类的对象的地址可以赋给指向基类的指针
- 只能使用基类原有的成员
- 多态形成的条件
- 静多态: 函数重载,也是一种多态现象,通过命名倾轧在编译阶段决定,故称静多态
- 动多态: 在运行阶段决定 条件是
- 父类中有虚函数
- 子类override(覆写)父类中的虚函数
- 用已被子类对象赋值的父类指针或引用(对象不行),调用共用接口(发生虚函数调用)
- 虚函数
1
2
3
4
5
6
7
8
9
|
class A
{
public:
virtual void func();//只需在声明时加;
};
//父类时有虚函数时,子类覆写override虚函数时(同名同参同返回),可以不加virtual,但建议加上
//将子类对象赋值给父类引用或指针时,用这个父类指针或引用访问的虚函数为子类的虚函数(如果有的话)
//子类中虚函数的访问权限可以不和父类一致
//覆写范围是很大的,如果不覆写默认从父类继承
|
- 纯虚函数
1
2
3
4
5
6
7
8
|
class A
{
virtual void func()=0;//格式,没有实现体
};
//含有纯虚函数的类,称为抽象基类(java中叫interface),不可以实例化(A a; 不可以)
//作用就是给族类提供接口用的,抽象基类不能实例化,但可以用指针和引用指向一个子类,实现多态
//抽象基类存在的意义就是被继承
//如果派生类中没有对该函数override,则这个函数在派生类中仍为纯虚函数,派生类仍为纯虚基类
|
- 含有虚函数的析构: 虚析构,就是为了delete基类指针时将子类对象析构完全
- 含有虚函数的类,析构函数也应该声明为虚函数
- 在delete父类指针的时候,会调用子类的析构函数,实现完整析构
- 当一个类中有虚函数的时候,请将析构器声明为vritual
- 若干限制
- 静态成员函数不能是虚函数,静态成员函数不受对象的捆绑,只有类的信息
- 内联函数,普通函数,不能是虚函数
- 构造函数不能是虚函数
- 析构函数通常声明为虚函数
- 设计模式 – 依赖倒置原则
//定义,高层模块不应该依赖底层模块,二者都应该依赖抽象
//抽象不应该依赖细节,细节应该依赖抽象
//核心:面向接口编程
- 虚函数表 virtual table
- 类实例化时,内存最前面先放一个指向v-table的指针,然后是数据成员
- v-table中,先放父类虚函数,再放子类虚函数
- 如果子类override了父类虚函数,则替换虚函数表相应位置的函数指针
- 多态实现的前题,一定是生成完了v-table之后,所以在父类构造器调用虚函数实现不了多态
run time type identificaition。运行时信息,来自于多态,所以一下运算符只用于基于多态的继承体系中
- typeid运算符
1
2
3
4
5
6
7
8
9
|
//使用这个运算符 要包含头文件<typeinfo>
//返回包含数据信息的type_info对象引用
//type_info重载了==,!=来比较是否相等,函数name()返回类型名称
//type_info的拷贝赋值为私有,不可操作,只用来做调试
B b;
cout<<typeid(b).name()<<endl;//输出b信息
A * a=&b;
cout<<typeid(*a).name()<<endl;//输出b信息
//可以帮你辨别一个指针到底指向谁,在多态中
|
static_cast<>()
- 在一个方向上可以做隐式转换,在另一个方向方向可以做静态转换
- 发生在编译阶段,不保证后续使用的正确性
- 用于父子类不安全
reinterpret_cast<>()
- 既不在编译期也不再运行期进行检查,安全性完全由程序员决定
dynamic_cast<>()
- 运行时的类型转换方法,检查指针所指类型,然后判断是否与要转换的类型有一种“is a”的关系
- 如果是,返回对象地址,不是返回null
- 只用于含有虚函数的父子类中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
//泛型(Generic Programming):多种数据类型上皆可操作的含义
//语法格式
template<typename/class 类型参数表>
返回类型 函数模板名(函数参数表)
{
函数模板实现体
}
template<typename T,typename T2....>
void swapp(T a,T b)
{
a^=b;b^=a;a^=b;
}
template<typename T>
class stackk{
stackk();
};
template<typename T>
stackk::stackk<T>()
{}
//定义实现分开时,都要顶着帽子,(写类时,函数在外部实现也要戴帽子,并且函数名后加<T>)
//模板适用于参数个数相同,函数体相同的情况
//参数个数如果不同,不要使用模板
|
流类对象都重载了 <<
和 >>
- 输入输出IO流
- IO对象的构造器和赋值重载是private,不能拷贝或赋值,使用时只能传引用或指针
- IO对象是由缓冲区的,缓冲区写满或遇到endl才写入,(cin背后是键盘,cout背后是屏幕)
- endl,flush,unitbuf 都可以刷缓冲
- 标准输出 cout cerr clog
1
2
3
4
|
//cout.unsetf(ios::dec);
//cout.setf(ios::hex);
//<iomanip> 流算子头文件,上面并不好用
//cout<<hed<<a<<endl;
|
- 标准输入 cin
1
2
3
4
5
6
7
|
//istream 成员函数
cin.get();//读一个字符返回,包括空格回车tab空字符
cin.get(buf,1024,'\n');
cin.getline(buf,1024,'默认回车');
istream& ignore(streamsize n=1,int delim=EOF);//跳过n个或遇到delim为止,delim也被跳过
int peek();//窥视 当前指针不改动,返回内容(int 型),自己转char
istream& putback(char c);//回推指针,并在此位置替换为c
|
- 文件IO流
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
ifstream ifile("1.txt");//只能从文件读入 默认模式 ios::in
if(!ifile)cout<<"打开失败";
ofstream ofile("2.txt");//只能写到文件 默认模式 ios::out|ios::trunc
//没有这个文件自动创建
fstream iofile("3.txt");//默认模式 ios::in|ios::out|ios::app
iofile.close();//刷缓冲区
ios::in ios::out //不会自动创建文件
ios::trunc //覆盖原来 会自动创建文件
ios::app //追加 会自动创建文件
ios::binary //二进制方式
ios::cur //文件当前位置
ios::end //文件末尾
f.seekg(50,ios::cur);//seekget 从当前位置向后50
f.seekp(-50,ios::end);//seekput 从文件尾向前50
//标识
f.eof();//达到文件尾,返回true
f.bad();//读写过程出错,返回true
f.fail();//除了和bad()一样,格式错误(例如想要读整数,得到字母),遇到eof都返回true
f.good();//上面任何返回true,则返回false
f.clear();//将所有标记位置位正常,不是清空流
cin.sync();//清空流
//遍历读,一个个读
char c;
while(f.get(c),!f.eof()){
f2.put(c);//写入另一个文件
}
//一行一行读
char buf[1024];
while(f.getline(buf,1024,'\n'),!f.eof()){
f2<<buf<<endl;//用get不会跳过\n,用getline \n被跳过,还要补;
}
//byte流读
ostream & write(const char * buffer,int len);
istream & read(const char * buffer,int len);
struct stu{
char name[100];
char sex;
int age;
};
stu s[5]=
{
{"as",'y',12},
{"gg",'x',1},
{"rr",'y',3},
{"ww",'x',100},
{"qq",'x',107}
};
fstream f("asd.binary",ios::in|ios::out|ios::trunc|ios::binary);
//f.write(s,sizeof(s));
for(int i=0;i<5;i++)
{
f.write((char *)&s[i],sizeof(stu));
}
f.seekg(0,ios::beg);
stu tmp;
while(f.read((char*)&tmp,sizeof(stu)),!f.eof()){
cout<<tmp.name<<endl;;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
//在可能出错的地方使用try-catch结构
void func(int x){
if(x<0)
throw -1;//在try下面调用的函数中可以抛出异常,终止程序往下执行
//程序立即转到上面捕获异常的地方
//异常传递是一个拷贝,如果自定义类型,注意拷贝构造,
}
try{
func(3);
}catch(int e)//如果下方扔出的异常不是int型,将接不到,程序调用另一个函数,终止整个程序
{//可以自己写异常信息类型,这样能得到更多信息
cout<<"catch a exception "<<e<<endl;
}//catch执行完毕,继续执行后面的语句,如果没有匹配,系统调用terminate终止程序
//可以有多个catch
catch(...){
//如果上面都没有接到,这个来接,有这个的时候,系统就不会调用terminate
}
//如果有多层try-catch结构,底层抛出一个异常,上层没有人接的话,继续传给上上层
//如果直到尽头都没有人接,系统才调用terminate
//为了加强程序的可读性,可以在函数声明中列出可能抛出的所有异常类型
//例如:
void func() throw(int ,A ,B ,double);//括号没内容表示不抛出异常
//栈自旋 throw抛出异常时,会在离开栈空间时销毁栈,不执行后面的代码,但看作函数执行完毕,退出销毁
//这也是为什么throw不传引用的原因
|
===========================华丽的分割线===================================
全剧终。。。
至此你看完了大部分c++ 没错,是看!!!