【本文谢绝转载】

C++类型转换

         类型转换1static_cast

         static_cast的局限性

         类型转换2 强制类型转换,reinterpret_cast

         类型转换3dynamic_cast 类的类型转换,演示:自上而下

         static_cast  reinterpret_cast 转换的对比

         const_cast,将常量类型转换为变量,经典案例

         类型转换4const_cast,修改无法修改的内存,直接宕掉

         总结

异常 

         异常将严格按照类型匹配

         1,基本语法

         2,发送异常,不去接收,程序会宕掉

         函数接收到异常,继续往外抛:

         异常可以跨函数:

         异常:1栈解旋,可以执行类的析构函数

         异常接口说明1:指定抛出异常的种类

         异常接口说明2 不允许抛出异常

         异常接口说明3:不限制抛出异常

         异常类型生命周期 1,传统的函数报错方式:

         异常类型生命周期 2,普通的异常抛出

         异常类型生命周期 3,指针类型

         异常类型生命周期 3,类异常

         这个e是谁抛出的?

         【结论】把抛出的匿名对象,拷贝给e对象

         【证明】把抛出的匿名对象,拷贝给e对象

         结论2: 使用引用的话 会使用throw时候的那个对象

         结论3: 指针可以和引用/元素写在一块 但是引用/元素不能写在一块

         结论3: 指针可以和引用/元素写在一块 但是引用/元素不能写在一块

         需要显式调用delete

         结论4: 类对象时, 使用引用比较合适

         【异常的层次结构·老方法】

         【异常的层次结构·推荐方法】类的多态实现

         C++标准程序库异常

         继承C++编制异常库:实现函数异常

 

标准输入输出IO

         标准输入

                   1,基本输入输出

                   cin.get()从缓冲区读取,输出到屏幕:(存在IO阻塞)

                   cin.get()如果缓冲区有数据,就自动读取

                   cin.getline读取一行

                   cin.ignore函数作用是跳过输入流中n个字符,

                   cin.peek()查看缓冲区有没有内容

                   cin.putback把收到的字符返回去,可以再读一次

         标准输出

                   cout.put()输出单个字符

                   cout.write标准输出

                   cout的格式化输出 1

                   cout格式输出2

                   cout格式输出3

文件IO

         1,文件写入

         文件写,文件读操作

         文件追加写入fname,ios::app

         文件二进制的读写:

        

作业练习        

1 编程实现以下数据输入/输出:

   (1)以左对齐方式输出整数,域宽为12

   (2)以八进制、十进制、十六进制输入/输出整数。

   (3)实现浮点数的指数格式和定点格式的输入/输出,并指定精度。

   (4)把字符串读入字符型数组变量中,从键盘输入,要求输入串的空格也全部读入,以回车符结束。

   (5)将以上要求用流成员函数和操作符各做一遍。

   

2编写一程序,将两个文件合并成一个文件。

3编写一程序,统计一篇英文文章中单词的个数与行数。

4编写一程序,将C++源程序每行前加上行号与一个空格。

4.5编写一程序,输出 ASCII码值从20127ASCII码字符表,格式为每行10个。


---------------------------------------------------------------


类型转换1static_cast

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;

int main()
{
	double pi = 3.1415926;
	int num1 = (int)pi;		//C风格
	int num2 = static_cast<int>(pi);	//C++风格1,静态类型转换,编译器会做类型检查
	int num3 = pi;			//C语言中 隐式类型转换的地方均可以使用static_cast进行类型转换
	cout << num1 << endl;
	cout << num2 << endl;
	cout << num3 << endl;



	return 0;
}


chunli@http://990487026.blog.51cto.com~/c++$ g++ -g -o run main.cpp && ./run 
3
3
3
chunli@http://990487026.blog.51cto.com~/c++$



类型转换1static_cast的局限性

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;

int main()
{
	char p1[] = "Hello World!";
	int  *p2 = NULL;
	p2 = static_cast<int *>(p1);

	return 0;
}


chunli@http://990487026.blog.51cto.com~/c++$ g++ -Wwrite-strings -g -o run main.cpp && ./run 
main.cpp: In function ‘int main()’:
main.cpp:8:28: error: invalid static_cast from type ‘char [13]’ to type ‘int*’
  p2 = static_cast<int *>(p1);
                            ^
chunli@http://990487026.blog.51cto.com~/c++$


类型转换2在不同类型之间强制类型转换reinterpret_cast

不能将这个作为普通数据类型的转换

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;

int main()
{
	char p1[] = "Hello World!";
	int  *p2 = NULL;
	p2 = reinterpret_cast <int *>(p1);
	cout << *p2 << endl;

	return 0;
}


chunli@http://990487026.blog.51cto.com~/c++$ g++ -Wwrite-strings -g -o run main.cpp && ./run 
1819043144
chunli@http://990487026.blog.51cto.com~/c++$



类型转换3 dynamic_cast 类的类型转换演示自上而下

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;

class Animal
{
public:
	virtual void cry() = 0;
};

class Dog : public Animal
{
public:
	virtual void cry()
	{
		cout << "汪汪" << endl;
	}
	void doHome()
	{
		cout << "看家" << endl;
	}
};

class Cat : public Animal
{
public:
	virtual void cry()
	{
		cout << "喵喵" << endl;
	}
	void doThing()
	{
		cout << "抓老鼠" << endl;
	}
};

void playObj(Animal *base)
{
	base->cry(); // 1有继承 2虚函数重写 3 父类指针 指向子类对象  ==>多态
	//能识别子类对象
	// dynamic_cast 运行时类型识别  RIIT
	
	Dog *pDog = dynamic_cast<Dog *>(base);
	if (pDog != NULL)
	{
		pDog->doHome(); //让够 做自己 特有的工作 
	}

	Cat *pCat = dynamic_cast<Cat *>(base);	//父类对象 ===> 子类对象 
											//向下转型  
											//把老子 转成 小子 
	if (pCat != NULL)
	{
		pCat->doThing();  //让够 做自己 特有的工作 
	}
}


int main()
{
	Animal *pBase = NULL;
	Dog d1;
	Cat c1;
	playObj(&d1);
	playObj(&c1);
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
汪汪
看家
喵喵
抓老鼠
chunli@http://990487026.blog.51cto.com~/c++$


static_cast  reinterpret_cast 转换的对比

把大树转换成动物会报错因为编译器会对static_cast做检查

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;

class Tree {};

class Animal
{
public:
	virtual void cry() = 0;
};

class Dog : public Animal
{
public:
	virtual void cry()
	{
		cout << "汪汪" << endl;
	}
	void doHome()
	{
		cout << "看家" << endl;
	}
};

class Cat : public Animal
{
public:
	virtual void cry()
	{
		cout << "喵喵" << endl;
	}
	void doThing()
	{
		cout << "抓老鼠" << endl;
	}
};

int main()
{
	Dog d1;
	Animal *pBase = &d1;
	pBase = static_cast<Animal *>(&d1); 		//让C++编译在编译的时候进行 类型检查 
	pBase = reinterpret_cast<Animal *>(&d1); 	//强制类型转换 

	Tree t1;
	pBase = static_cast<Animal *>(&t1); 		// C++编译器会做类型检查
	pBase = reinterpret_cast<Animal *>(&t1);  	//reinterpret_cast 重新解释 ....强制类转换的味道
	
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
main.cpp: In function ‘int main()’:
main.cpp:46:35: error: invalid static_cast from type ‘Tree*’ to type ‘Animal*’
  pBase = static_cast<Animal *>(&t1);   // C++编译器会做类型检查
                                   ^
chunli@http://990487026.blog.51cto.com~/c++$


static_cast  reinterpret_cast 转换的对比

屏蔽 static_cast编译就通过

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;

class Tree {};

class Animal
{
public:
	virtual void cry() = 0;
};

class Dog : public Animal
{
public:
	virtual void cry()
	{
		cout << "汪汪" << endl;
	}
	void doHome()
	{
		cout << "看家" << endl;
	}
};

class Cat : public Animal
{
public:
	virtual void cry()
	{
		cout << "喵喵" << endl;
	}
	void doThing()
	{
		cout << "抓老鼠" << endl;
	}
};

int main()
{
	Dog d1;
	Animal *pBase = &d1;
	pBase = static_cast<Animal *>(&d1); 		//让C++编译在编译的时候进行 类型检查 
	pBase = reinterpret_cast<Animal *>(&d1); 	//强制类型转换 

	Tree t1;
	//pBase = static_cast<Animal *>(&t1); 		// C++编译器会做类型检查
	pBase = reinterpret_cast<Animal *>(&t1);  	//reinterpret_cast 重新解释 ....强制类转换的味道
	
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
chunli@http://990487026.blog.51cto.com~/c++$


类型转换4 const_cast将常量类型转换为变量经典案例

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;

void fun(const char *p)
{
	char *p2 = const_cast<char *>(p);
	p2[0] = 'A';
}

int main()
{
	char p1[] = "Hello World!";
	fun(p1);
	cout << p1 << endl;
	
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
Aello World!
chunli@http://990487026.blog.51cto.com~/c++$


类型转换4 const_cast修改无法修改的内存直接宕掉

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;

void fun(const char *p)
{
	char *p2 = const_cast<char *>(p);
	p2[0] = 'A';
}

int main()
{
	const char  *p1 = "Hello World!";
	fun(p1);
	cout << p1 << endl;
	
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
Segmentation fault (core dumped)
chunli@http://990487026.blog.51cto.com~/c++$



总结

结论1程序员要清除的知道: 要转的变量类型转换前是什么类型类型转换后是什么类型。转换后有什么后果。

结论2一般情况下不建议进行类型转换避免进行类型转换。




异常

1异常是一种程序控制机制与函数机制独立和互补

  函数是一种以栈结构展开的上下函数衔接的程序控制系统,异常是另一种控制结构,它依附于栈结构,却可以同时设置多个异常类型作为网捕条件,从而以类型匹配在栈机制中跳跃回馈.

2异常设计目的

   栈机制是一种高度节律性控制机制,面向对象编程却要求对象之间有方向、有目的的控制传动,从一开始异常就是冲着改变程序控制结构以适应面向对象程序更有效地工作这个主题而不是仅为了进行错误处理。

异常设计出来之后却发现在错误处理方面获得了最大的好处

异常将严格按照类型匹配


异常的基本思想

wKiom1eE6SGg8wjgAABkbtt8zjE715.png

1C++的异常处理机制使得异常的引发异常的处理不必在同一个函数中这样底层的函数可以着重解决具体问题而不必过多的考虑异常的处理。上层调用者可以再适当的位置设计对不同类型异常的处理。

2异常是专门针对抽象编程中的一系列错误处理的C++中不能借助函数机制因为栈结构的本质是先进后出依次访问无法进行跳跃但错误处理的特征却是遇到错误信息就想要转到若干级之上进行重新尝试如图

wKiom1eE6VOQWyxLAACCW5vvl34267.png




1基本语法

wKiom1eE6XeiuJF6AADGWAKkOVI914.png

1 若有异常则通过throw操作创建一个异常对象并抛掷。

2 将可能抛出异常的程序段嵌在try块之中。控制通过正常的顺序执行到达try语句然后执行try块内的保护段。

3 如果在保护段执行期间没有引起异常那么跟在try块后的catch子句就不执行。程序从try块后跟随的最后一个catch子句后面的语句继续执行下去。

4 catch子句按其在try块后出现的顺序被检查。匹配的catch子句将捕获并处理异常或继续抛掷异常。

5 如果匹配的处理器未找到则运行函数terminate将被自动调用其缺省功能是调用abort终止程序。

6处理不了的异常可以在catch的最后一个分支使用throw语法向上扔。

 

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;

void divide(int x, int y)
{
	if (y == 0)
	{
		throw x; //抛出 int类型 异常
	}

	cout << "divide运算结果:" << x/y<< endl;
}

int main()
{
	try
	{
		divide(10, 2);
		divide(100, 0);

	}
	catch (int e)
	{
		cout << e << "除数是零" << endl;
	}
	catch ( ... )  
	{
		cout <<  "其他未知类型异常 "<< endl;
	}
	
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
divide运算结果:5
100除数是零
chunli@http://990487026.blog.51cto.com~/c++$


2发送异常不去接收程序会宕掉

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;

void divide(int x, int y)
{
	if (y == 0)
	{
		throw x; //抛出 int类型 异常
	}

	cout << "divide运算结果:" << x/y<< endl;
}

int main()
{
	divide(100, 0);
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
terminate called after throwing an instance of 'int'
Aborted (core dumped)
chunli@http://990487026.blog.51cto.com~/c++$


函数接收到异常继续往外抛

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;

void divide(int x, int y)
{
	if (y == 0)
	{
		throw x; //抛出 int类型 异常
	}

	cout << "divide运算结果:" << x/y<< endl;
}

void myDivide(int x, int y)
{
	try
	{
		divide(x, y);
	}
	catch (...)
	{
		cout << "我接受了 divide的异常 但是我没有处理 我向上抛出" << endl;
		throw ;
	}
}


int main()
{
	try
	{
		myDivide(100, 0);
	}
	catch (int e)
	{
		cout << e << "除数是0" << endl;
	}
	catch ( ... )  //
	{
		cout <<  "其他未知类型异常 "<< endl;
	}
	

	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
我接受了 divide的异常 但是我没有处理 我向上抛出
100除数是0
chunli@http://990487026.blog.51cto.com~/c++$


异常可以跨函数

hunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;

void divide(int x, int y)
{
	if (y == 0)
	{
		throw x; //抛出 int类型 异常
	}

	cout << "divide运算结果:" << x/y<< endl;
}

void myDivide(int x, int y)
{
	divide(x, y);
}


int main()
{
	try
	{
		myDivide(100, 0);
	}
	catch (int e)
	{
		cout << "除数是"<< e << " 被除数是0" << endl;
	}
	catch ( ... )  //
	{
		cout <<  "其他未知类型异常 "<< endl;
	}
	

	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
除数是100 被除数是0
chunli@http://990487026.blog.51cto.com~/c++$



异常1栈解旋可以执行类的析构函数

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;

class Test
{
public:
	Test(int a=0, int b=0)
	{
		this->a = a;
		this->b = b;
		cout << "构造函数do \n";
	}
	~Test()
	{
		cout << "析构函数do \n";
	}
private:
	int a;
	int b;
};

void myDivide() 
{
	Test t1(1, 2), t2(3, 4);
	cout << "myDivide ...要发生异常\n" ;
	throw 1;
	cout << "这句话没有机会执行\n" ;
}


int main()
{
	try
	{
		myDivide();
	}
	catch (int e)
	{
		cout << "除数是"<< e << " 被除数是0" << endl;
	}
	catch ( ... )  //
	{
		cout <<  "其他未知类型异常 "<< endl;
	}
	

	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
构造函数do 
构造函数do 
myDivide ...要发生异常
析构函数do 
析构函数do 
除数是1 被除数是0
chunli@http://990487026.blog.51cto.com~/c++$


异常接口说明1指定抛出异常的种类

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;

void fun1() throw(int ,char,char *)
{
	throw 99;
}
void fun2() throw()
{
	throw 99;
}
void fun3() 
{
	throw 3.14;
}

int main()
{
	try
	{
		fun1();
	}
	catch (int)
	{
		cout <<"int 类型" << endl;
	}
	catch (double)
	{
		cout <<"double 类型" << endl;
	}
	catch ( ... )  
	{
		cout <<  "其他未知类型异常 "<< endl;
	}
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
int 类型
chunli@http://990487026.blog.51cto.com~/c++$



异常接口说明2 不允许抛出异常

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;


void fun1() throw(int ,char,char *)
{
	throw 99;
}
void fun2() throw()
{
	throw 99;
}
void fun3() 
{
	throw 3.14;
}

int main()
{
	try
	{
		fun2();
	}
	catch (int)
	{
		cout <<"int 类型" << endl;
	}
	catch (double)
	{
		cout <<"double 类型" << endl;
	}
	catch ( ... )  
	{
		cout <<  "其他未知类型异常 "<< endl;
	}
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
terminate called after throwing an instance of 'int'
Aborted (core dumped)
chunli@http://990487026.blog.51cto.com~/c++$



异常接口说明3不限制抛出异常

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;


void fun1() throw(int ,char,char *)
{
	throw 99;
}
void fun2() throw()
{
	throw 99;
}
void fun3() 
{
	throw 3.14;
}

int main()
{
	try
	{
		fun3();
	}
	catch (int)
	{
		cout <<"int 类型" << endl;
	}
	catch (double)
	{
		cout <<"double 类型" << endl;
	}
	catch ( ... )  
	{
		cout <<  "其他未知类型异常 "<< endl;
	}
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
double 类型
chunli@http://990487026.blog.51cto.com~/c++$



异常类型生命周期 1传统的函数报错方式

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;

//传统的错误处理机制,假设字符串首字母是a就报错
int my_strcpy(char *to, char *from)
{
	if (from == NULL)
	{
		return 1;
	}
	if (to == NULL)
	{
		return 2;
	}

	if (*from == 'a')
	{
		cout << "处理类型异常 \n";
		return 3;
	}

	while (*from != '\0')
	{
		*to = *from;
		to ++;
		from ++;
	}
	*to = '\0';
}

int main()
{
	int ret = 0;
	char buf1[] = "abcdefg";
	char buf2[1024] = {0};

 	ret = my_strcpy(buf2, buf1);
	if (ret != 0)
	{
		switch(ret)
		{
		case 1:
			cout << "源buf出错!\n";
			break;
		case 2:
			cout <<"目的buf出错!\n";
			break;
		case 3:
			cout << "copy过程出错!\n";
			break;
		default:
			cout <<"未知错误!\n";
			break;
		}
	}
	cout << "buf2:" << buf2;
	
	return  0;
}



chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
处理类型异常 
copy过程出错!
buf2:chunli@http://990487026.blog.51cto.com~/c++$



异常类型生命周期 2普通的异常抛出

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;

void my_strcpy(char *to, char *from)
{
	if (from == NULL)
	{
		throw 1;
	}
	if (to == NULL)
	{
		throw 1.1;
	}

	if (*from == 'a')
	{
		cout << "处理类型异常 \n";
		throw 'A';
	}

	while (*from != '\0')
	{
		*to = *from;
		to ++;
		from ++;
	}
	*to = '\0';
}

int main()
{
	int ret = 0;
	char buf1[] = "abcdefg";
	char buf2[1024] = {0};
	try{
 		my_strcpy(buf2, buf1);
	}
	catch (int e) //e可以写 也可以不写
	{
		cout << e << "int类型异常" << endl;
	}
	catch(char  e)
	{
		cout << "char类型异常" << endl;
	}
	catch(char *e)
	{
		cout << e << "char* 类型异常" << endl;
	}
	catch(...)
	{
		cout <<" ............. " << endl;
	}


	return  0;
}



chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
处理类型异常 
char类型异常
chunli@http://990487026.blog.51cto.com~/c++$


异常类型生命周期 3指针类型

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;

void my_strcpy(char *to, char *from)
{
	if (from == NULL)
	{
		throw "哈哈1处异常";
	}
	if (to == NULL)
	{
		throw "哈哈2处异常";
	}

	if (*from == 'a')
	{
		throw "哈哈3处异常";
	}
}

int main()
{
	char p1[] = "apple";
	char p2[10] = {0};
	try{
 		my_strcpy(p2,p1);
	}
	catch(const char *e)
	{
		cout << e <<  endl;
	}
	catch(...)
	{
		cout <<"............. " << endl;
	}
	return  0;
}



chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
哈哈3处异常
chunli@http://990487026.blog.51cto.com~/c++$



异常类型生命周期 3类异常

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;
class a{};
class b{};
class c{};

void my_strcpy(char *to, char *from)
{
	if (from == NULL)
	{
		throw a();//抛出匿名对象
	}
	if (to == NULL)
	{
		throw b();
	}

	if (*from == 'a')
	{
		throw c();
	}
}

int main()
{
	char p1[] = "apple";
	char p2[10] = {0};
	try{
 		my_strcpy(p2,p1);
	}
	catch(a e)
	{
		cout << "a type error " <<  endl;
	}
	catch(b e)
	{
		cout << "b type error " <<  endl;
	}
	catch(c e)
	{
		cout << "c type error " <<  endl;
	}
	catch(...)
	{
		cout <<"............. " << endl;
	}
	return  0;
}



chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
c type error 
chunli@http://990487026.blog.51cto.com~/c++$


问题引出

catch(a e)

catch(b e)

catch(c e)

这个e是谁抛出的

是把匿名的值拷给e 还是引用给e

【复杂思考】

catch(c *e)

catch(c &e)






【结论】把抛出的匿名对象拷贝给e对象

【证明】

结论1: 如果 接受异常的时候 使用一个异常变量,则copy构造异常变量.

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;
class c{
public:
	c()
	{
		cout << "构造函数 \n";
	}
	c(const c &obj)
	{
		cout << "拷贝函数 \n";
	}
	~c()
	{
		cout << "析构函数 \n";
	}
};

void my_strcpy()
{
	throw c();
}

int main()
{
	try{
 		my_strcpy();
	}
	catch(c e)
	{
		cout << "c type error " <<  endl;
	}
	catch(...)
	{
		cout <<"............. " << endl;
	}
	return  0;
}



chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
构造函数 
拷贝函数 
c type error 
析构函数 
析构函数 
chunli@http://990487026.blog.51cto.com~/c++$



结论2: 使用引用的话 会使用throw时候的那个对象

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;
class c{
public:
	c()
	{
		cout << "构造函数 \n";
	}
	c(const c &obj)
	{
		cout << "拷贝函数 \n";
	}
	~c()
	{
		cout << "析构函数 \n";
	}
};

void my_strcpy()
{
	throw c();
}

int main()
{
	try{
 		my_strcpy();
	}
	catch(c &e)
	{
		cout << "c type error " <<  endl;
	}
	catch(...)
	{
		cout <<"............. " << endl;
	}
	return  0;
}



chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
构造函数 
c type error 
析构函数 
chunli@http://990487026.blog.51cto.com~/c++$


结论3: 指针可以和引用/元素写在一块 但是引用/元素不能写在一块

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;
class c{
public:
	c()
	{
		cout << "构造函数 \n";
	}
	c(const c &obj)
	{
		cout << "拷贝函数 \n";
	}
	~c()
	{
		cout << "析构函数 \n";
	}
};

void my_strcpy()
{
	throw c();
}

int main()
{
	try{
 		my_strcpy();
	}
	catch(c e)
	{
		cout << "c type error " <<  endl;
	}
	catch(c &e)
	{
		cout << "c type error " <<  endl;
	}
	catch(...)
	{
		cout <<"............. " << endl;
	}
	return  0;
}



chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
main.cpp: In function ‘int main()’:
main.cpp:33:2: warning: exception of type ‘c’ will be caught [enabled by default]
  catch(c &e)
  ^
main.cpp:29:2: warning:    by earlier handler for ‘c’ [enabled by default]
  catch(c e)
  ^
构造函数 
拷贝函数 
c type error 
析构函数 
析构函数 
chunli@http://990487026.blog.51cto.com~/c++$


结论3: 指针可以和引用/元素写在一块 但是引用/元素不能写在一块

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;
class c{
public:
	c()
	{
		cout << "构造函数 \n";
	}
	c(const c &obj)
	{
		cout << "拷贝函数 \n";
	}
	~c()
	{
		cout << "析构函数 \n";
	}
};

void my_strcpy()
{
	throw new c();
}

int main()
{
	try{
 		my_strcpy();
	}
	catch(c e)
	{
		cout << "c type error " <<  endl;
	}
	catch(c *e)
	{
		cout << "c* type error " <<  endl;
	}
	catch(...)
	{
		cout <<"............. " << endl;
	}
	return  0;
}



chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
构造函数 
c* type error 
chunli@http://990487026.blog.51cto.com~/c++$ 
出现了野指针


需要显式调用delete

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
using namespace std;
class c{
public:
	c()
	{
		cout << "构造函数 \n";
	}
	c(const c &obj)
	{
		cout << "拷贝函数 \n";
	}
	~c()
	{
		cout << "析构函数 \n";
	}
};

void my_strcpy()
{
	throw new c();
}

int main()
{
	try{
 		my_strcpy();
	}
	catch(c e)
	{
		cout << "c type error " <<  endl;
	}
	catch(c *e)
	{
		delete e;
		cout << "c* type error " <<  endl;
	}
	catch(...)
	{
		cout <<"............. " << endl;
	}
	return  0;
}



chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
构造函数 
析构函数 
c* type error 
chunli@http://990487026.blog.51cto.com~/c++$


【综合以上关于抛出类】

结论4: 类对象时, 使用引用比较合适




【异常的层次结构·老方法】

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
using namespace std;

class MyArray
{
public:
	MyArray(int len);
	~MyArray();
public:
	int & operator[](int index);
	int getLen();

	class eSize
	{
	public:
		eSize(int size)
		{
			m_size = size;
		}
		virtual void printErr()
		{
			cout << "size:" << m_size << " ";
		}
	protected:
		int m_size;
	};
	class eNegative : public eSize
	{
	public:
		eNegative(int size) : eSize(size)
		{
			;
		}
		virtual void printErr()
		{
			cout << "eNegative 类型 size:" << m_size << " ";
		}
	};
	class eZero : public eSize
	{
	public:
		eZero(int size) : eSize(size)
		{
			;
		}
		virtual void printErr()
		{
			cout << "eZero 类型 size:" << m_size << " ";
		}
	};
	class eTooBig : public eSize
	{
	public:
		eTooBig(int size) : eSize(size)
		{
			;
		}
		virtual void printErr()
		{
			cout << "eTooBig 类型 size:" << m_size << " ";
		}
	};
	class eTooSmall : public eSize
	{
	public:
		eTooSmall(int size) : eSize(size)
		{
			;
		}
		virtual void printErr()
		{
			cout << "eTooSmall 类型 size:" << m_size << " ";
		}
	};

private:
	int *m_space;
	int m_len;
};


MyArray::MyArray(int len)
{
	if (len  < 0)
	{
		throw eNegative(len);
	}
	else if (len == 0)
	{
		throw eZero(len);
	}
	else if (len > 1000)
	{
		throw eTooBig(len);
	}
	else if (len < 3)
	{
		throw eTooSmall(len);
	}
	m_len = len;
	m_space = new int[len];
}

MyArray::~MyArray()
{
	if (m_space != NULL)
	{
		delete [] m_space;
		m_space = NULL;
		m_len = 0;
	}
}

int & MyArray::operator[](int index)
{
	return m_space[index];
}

int MyArray::getLen()
{
	return m_len;
}

int main()
{
	
	try
	{
		MyArray a(-5);
		for (int i=0; i<a.getLen(); i++)
		{
			a[i] = i+1;
			printf("%d ", a[i]);
		}
	}
	catch(MyArray::eNegative e)
	{
		cout << "eNegative 类型异常" << endl;
	}
	catch(MyArray::eZero e)
	{
		cout << "eZero 类型异常" << endl;
	}
	catch(MyArray::eTooBig e)
	{
		cout << "eTooBig 类型异常" << endl;
	}
	catch(MyArray::eTooSmall e)
	{
		cout << "eTooSmall 类型异常" << endl;
	}
	
	catch (...)
	{
	}
	
	return 0 ;
}
chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
eNegative 类型异常
chunli@http://990487026.blog.51cto.com~/c++$


【异常的层次结构·推荐方法】类的多态实现

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
using namespace std;

class MyArray
{
public:
	MyArray(int len);
	~MyArray();
public:
	int & operator[](int index);
	int getLen();

	class eSize
	{
	public:
		eSize(int size)
		{
			m_size = size;
		}
		virtual void printErr()
		{
			cout << "size:" << m_size << " \n";
		}
	protected:
		int m_size;
	};
	class eNegative : public eSize
	{
	public:
		eNegative(int size) : eSize(size)
		{
			;
		}
		virtual void printErr()
		{
			cout << "eNegative 类型 size:" << m_size << " \n";
		}
	};
	class eZero : public eSize
	{
	public:
		eZero(int size) : eSize(size)
		{
			;
		}
		virtual void printErr()
		{
			cout << "eZero 类型 size:" << m_size << " \n";
		}
	};
	class eTooBig : public eSize
	{
	public:
		eTooBig(int size) : eSize(size)
		{
			;
		}
		virtual void printErr()
		{
			cout << "eTooBig 类型 size:" << m_size << " \n";
		}
	};
	class eTooSmall : public eSize
	{
	public:
		eTooSmall(int size) : eSize(size)
		{
			;
		}
		virtual void printErr()
		{
			cout << "eTooSmall 类型 size:" << m_size << " \n";
		}
	};

private:
	int *m_space;
	int m_len;
};


MyArray::MyArray(int len)
{
	if (len  < 0)
	{
		throw eNegative(len);
	}
	else if (len == 0)
	{
		throw eZero(len);
	}
	else if (len > 1000)
	{
		throw eTooBig(len);
	}
	else if (len < 3)
	{
		throw eTooSmall(len);
	}
	m_len = len;
	m_space = new int[len];
}

MyArray::~MyArray()
{
	if (m_space != NULL)
	{
		delete [] m_space;
		m_space = NULL;
		m_len = 0;
	}
}

int & MyArray::operator[](int index)
{
	return m_space[index];
}

int MyArray::getLen()
{
	return m_len;
}

int main()
{

	try
	{
		MyArray a(-5);
		for (int i=0; i<a.getLen(); i++)
		{
			a[i] = i+1;
			printf("%d ", a[i]);
		}
	}
	catch(MyArray::eSize &e)
	{
		//cout <<  "len的大小: " << e.eSize();
		e.printErr();
	}
	catch (...)
	{
	}
	return 0 ;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
eNegative 类型 size:-5 
chunli@http://990487026.blog.51cto.com~/c++$


C++标准程序库异常见图

wKioL1eE6dGQS-HZAAC8wa0x8IM873.png


wKiom1eE6fKQH__zAAGRyO74LsI077.png

wKioL1eE6hahAk8PAAJMxshAxe8794.png

wKiom1eE6jSxhEz9AAEyCVO7oHA098.png




wKiom1eE6nTBKh6LAAHAcVx6UxU930.png

wKioL1eE6sfSsz2AAAHAcVx6UxU881.png

演示

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include "iostream"
using namespace std;
#include <stdexcept> 
#include "string"

class Teacher
{
public:
	Teacher(int age)
	{
		if (age > 100)
		{
			throw out_of_range("年龄太大");
		}
		this->age = age;
	}
	int age;
};

int  main()
{
	try
	{
		Teacher t1(102);
	}
	catch (out_of_range e)
	{
		cout << e.what() << endl;
	}
	return 0;
}
chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
年龄太大
chunli@http://990487026.blog.51cto.com~/c++$


继承C++编制异常库实现函数异常

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include "iostream"
using namespace std;
#include <stdexcept> 
#include "string"

class MyException : public exception
{
public:
	MyException(const char *p)
	{
		this->m_p = p;
	}

	virtual const char * what()
	{
		cout << "MyException: 类型 " << m_p << endl;
		return m_p;
	}
	const char *m_p;
};

void testMyExcept()
{
	throw MyException("函数异常");
}
int  main()
{
	try
	{
		testMyExcept();
	}
	catch (MyException & e)
	{
		e.what();
	}
	catch (...)
	{
		cout << "未知 类型 " << endl;
	}
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
MyException: 类型 函数异常
chunli@http://990487026.blog.51cto.com~/c++$






IO流


I/O流的概念和流类库的结构

程序的输入指的是从输入文件将数据传送给程序程序的输出指的是从程序将数据传送给输出文件。

C++输入输出包含以下三个方面的内容

    对系统指定的标准设备的输入和输出。即从键盘输入数据输出到显示器屏幕。这种输入输出称为标准的输入输出简称标准I/O

    以外存磁盘文件为对象进行输入和输出即从磁盘文件输入数据数据输出到磁盘文件。以外存文件为对象的输入输出称为文件的输入输出简称文件I/O

    对内存中指定的空间进行输入和输出。通常指定一个字符数组作为存储空间(实际上可以利用该空间存储任何信息)。这种输入和输出称为字符串输入输出简称串I/O

 

C++I/OC的发展--类型安全和可扩展性

         C语言中用printfscanf进行输入输出往往不能保证所输入输出的数据是可靠的安全的。在C++的输入输出中编译系统对数据类型进行严格的检查凡是类型不正确的数据都不可能通过编译。因此C++I/O操作是类型安全(type safe)的。C++I/O操作是可扩展的不仅可以用来输入输出标准类型的数据也可以用于用户自定义类型的数据。

         C++通过I/O类库来实现丰富的I/O功能。这样使C++的输人输出明显地优于C 语言中的printfscanf但是也为之付出了代价C++I/O系统变得比较复杂要掌握许多细节。

         C++编译系统提供了用于输入输出的iostream类库。iostream这个单词是由3个部分组成的即i-o-stream意为输入输出流。在iostream类库中包含许多用于输入输出的类。常用的见表

wKioL1eE61jjbVTIAADwODzeXR0769.png


ios是抽象基类由它派生出istream类和ostream类两个类名中第1个字母io分别代表输入(input)和输出(output) istream类支持输入操作ostream类支持输出操作iostream类支持输入输出操作。iostream类是从istream类和ostream类通过多重继承而派生的类。其继承层次见上图表示。

C++对文件的输入输出需要用ifstrcamofstream类两个类名中第1个字母io分别代表输入和输出第2个字母f代表文件 (file)ifstream支持对文件的输入操作ofstream支持对文件的输出操作。类ifstream继承了类istreamofstream继承了类ostreamfstream继承了iostream。见图


wKiom1eE64Wwl9-TAABIU4pGIJc540.png


I/O类库中还有其他一些类但是对于一般用户来说以上这些已能满足需要了。

 

iostream类库有关的头文件

iostream类库中不同的类的声明被放在不同的头文件中用户在自己的程序中用#include命令包含了有关的头文件就相当于在本程序中声明了所需 要用到的类。可以换—种说法头文件是程序与类库的接口iostream类库的接口分别由不同的头文件来实现。常用的有

  • iostream      包含了对输入输出流进行操作所需的基本信息。

  • fstream      用于用户管理的文件的I/O操作。

  • strstream      用于字符串流I/O。

  • stdiostream      用于混合使用C和C     + +的I/O机制时例如想将C程序转变为C++程序。

  • iomanip      在使用格式化I/O时应包含此头文件。

iostream头文件中定义的流对象

iostream 头文件中定义的类有 iosistreamostreamiostreamistream _withassign ostream_withassigniostream_withassign 等。

 

iostream头文件中不仅定义了有关的类还定义了4种流对象

 

对象

含义

对应设备

对应的类

c语言中相应的标准文件

cin

标准输入流

键盘

istream_withassign

stdin

cout

标准输出流

屏幕

ostream_withassign

stdout

cerr

标准错误流

屏幕

ostream_withassign

stderr

clog

标准错误流

屏幕

ostream_withassign

stderr

 

iostream头文件中定义以上4个流对象用以下的形式以cout为例
    ostream cout ( stdout);
在定义coutostream流类对象时把标准输出设备stdout作为参数这样它就与标准输出设备(显示器)联系起来如果有
    cout <<3;
就会在显示器的屏幕上输出3

 

iostream头文件中重载运算符

“<<”“>>”本来在C++中是被定义为左位移运算符和右位移运算符的由于在iostream头文件中对它们进行了重载使它们能用作标准类型数据的输入和输出运算符。所以在用它们的程序中必须用#include命令把iostream包含到程序中。

    #include <iostream>

  • >>a表示将数据放入a对象中。

  • <<a表示将a对象中存储的数据拿出。

  • 标准I/O

标准I/O对象:cincoutcerrclog

cout流对象

contconsole output的缩写意为在控制台终端显示器的输出。强调几点。

1) cout不是C++预定义的关键字它是ostream流类的对象在iostream中定义。 顾名思义流是流动的数据cout流是流向显示器的数据。cout流中的数据是用流插入运算符“<<”顺序加入的。如果有
    cout<<"I "<<"study C++"<<"very hard. << “wang bao ming ";

按顺序将字符串"I ", "study C++ ", "very hard."插人到cout流中cout就将它们送到显示器在显示器上输出字符串"I study C++ very hard."cout流是容纳数据的载体它并不是一个运算符。人们关心的是cout流中的内容也就是向显示器输出什么。
2)“ccmt<<”输出基本类型的数据时可以不必考虑数据是什么类型系统会判断数据的类型并根据其类型选择调用与之匹配的运算符重载函数。这个过程都是自动的用户不必干预。如果在C语言中用prinf函数输出不同类型的数据必须分别指定相应的输出格式符十分麻烦而且容易出错。C++I/O机制对用户来说显然是方便而安全的。

3) cout流在内存中对应开辟了一个缓冲区用来存放流中的数据当向cout流插人一个endl时不论缓冲区是否已满都立即输出流中所有数据然后插入一个换行符并刷新流清空缓冲区。注意如果插人一个换行符”\n“cout<<a<<"\n"则只输出和换行而不刷新cout (但并不是所有编译系统都体现出这一区别。
4) iostream中只对"<<"">>"运算符用于标准类型数据的输入输出进行了重载但未对用户声明的类型数据的输入输出进行重载。如果用户声明了新的类型并希望用"<<"">>"运算符对其进行输入输出按照重运算符重载来做。

cerr流对象

cerr流对象是标准错误流cerr流已被指定为与显示器关联。cerr作用是向标准错误设备(standard error device)输出有关出错信息。cerr与标准输出流cout的作用和用法差不多。但有一点不同cout流通常是传送到显示器输出但也可以被重定向输出到磁盘文件而cerr流中的信息只能在显示器输出。当调试程序时往往不希望程序运行时的出错信息被送到其他文件而要求在显示器上及时输出这时应该用cerrcerr流中的信息是用户根据需要指定的。

clog流对象

clog流对象也是标准错误流它是console log的缩写。它的作用和cerr相同都是在终端显示器上显示出错信息。区别cerr是不经过缓冲区直接向显示器上输出有关信息而clog中的信息存放在缓冲区中缓冲区满后或遇endl时向显示器输出。

 

缓冲区的概念:

wKioL1eE66eTfu3fAABsoUCvKoA561.png

人们在输入输出时有一些特殊的要求如在输出实数时规定字段宽度只保留两位小数数据向左或向右对齐等。C++提供了在输入输出流中使用的控制符(有的书中称为操纵符)

wKiom1eE68nxMxtfAAHJjMOfZCE121.png

用流对象的成员函数控制输出格式

除了可以用控制符来控制输出格式外还可以通过调用流对象cout中用于控制输出格式的成员函数来控制输出格式。用于控制输出格式的常用的成员函数如下

wKioL1eE6-3zF5bBAADHqzyEa0U780.png

流成员函数setf和控制符setiosflags括号中的参数表示格式状态它是通过格式标志来指定的。格式标志在类ios中被定义为枚举值。因此在引用这些格式标志时要在前面加上类名ios和域运算符“::”

wKioL1eE7C2C61yPAAFUanosavM695.png



文件流类和文件流对象

输入输出是以系统指定的标准设备输入设备为键盘输出设备为显示器为对象的。在实际应用中常以磁盘文件作为对象。即从磁盘文件读取数据将数据输出到磁盘文件。

和文件有关系的输入输出类主要在fstream.h这个头文件中被定义在这个头文件中主要被定义了三个类由这三个类控制对文件的各种输入输出操作他们分别是ifstreamofstreamfstream其中fstream类是由iostream类派生而来他们之间的继承关系见下图所示。

wKioL1eE7FbThAOMAAAeBdWbInM466.png



C++文件的打开与关闭

打开文件

所谓打开(open)文件是一种形象的说法如同打开房门就可以进入房间活动一样。打开文件是指在文件读写之前做必要的准备工作包括

1为文件流对象和指定的磁盘文件建立关联以便使文件流流向指定的磁盘文件。

2指定文件的工作方式如该文件是作为输入文件还是输出文件是ASCII文件还是二进制文件等。

以上工作可以通过两种不同的方法实现。

 

1) 调用文件流的成员函数open。如

    ofstreamoutfile;  //定义ofstream(输出文件流类)对象outfile

   outfile.open("f1.dat",ios::out);  //使文件流与f1.dat文件建立关联

2行是调用输出文件流的成员函数open打开磁盘文件f1.dat并指定它为输出文件文件流对象outfile将向磁盘文件f1.dat输出数据。ios::outI/O模式的一种表示以输出方式打开一个文件。或者简单地说此时f1.dat是一个输出文件接收从内存输出的数据。

 

调用成员函数open的一般形式为

    文件流对象.open(磁盘文件名, 输入输出方式);

磁盘文件名可以包括路径如"c:\new\\f1.dat"如缺省路径则默认为当前目录下的文件。

 

2) 在定义文件流对象时指定参数

在声明文件流类时定义了带参数的构造函数其中包含了打开磁盘文件的功能。因此可以在定义文件流对象时指定参数调用文件流类的构造函数来实现打开文件的功能。如

   ostream outfile("f1.dat",ios::out); 一般多用此形式比较方便。作用与open函数相同。

输入输出方式是在ios类中定义的它们是枚举常量有多种选择


wKiom1eE7IODCcBIAAF_wFrdJvM619.png


案例2自学扩展思路

ofstream类的默认构造函数原形为

ofstream::ofstream(constchar *filename, intmode = ios::out,

 int penprot = filebuf::openprot);

 

·        filename  要打开的文件名

·        mode    要打开文件的方式

·        prot    打开文件的属性

  其中modeopenprot这两个参数的可选项表见下表

mode属性表

ios::app

以追加的方式打开文件

ios::ate

文件打开后定位到文件尾ios:app就包含有此属性

ios::binary

以二进制方式打开文件缺省的方式是文本方式。两种方式的区别见前文

ios::in

文件以输入方式打开

ios::out

文件以输出方式打开

ios::trunc

如果文件存在把文件长度设为0

 

可以用“|”把以上属性连接起来如ios::out|ios::binary

openprot属性表

属性

含义

0

普通文件打开访问

1

只读文件

2

隐含文件

4

系统文件



可以用或者“+”把以上属性连接起来 31|2就是以只读和隐含属性打开文件。

#include <fstream>
 usingnamespace std;
 
 int main() 
 {
         ofstream  myfile("c:\\1.txt",ios::out|ios::trunc,0);
          myfile<<"Hello "<<endl<<"网址"<<"@http://990487026.blog.51cto.comn";
         myfile.close()
         system("pause");
 }

 

文件使用完后可以使用close成员函数关闭文件。

ios::app为追加模式在使用追加模式的时候同时进行文件状态的判断是一个比较好的习惯。

#include <iostream>
 #include <fstream>
 usingnamespace std;
 int main() 
 {
         ofstream  myfile("c:\\1.txt",ios::app,0);
         if(!myfile)//或者写成myfile.fail()
         {
                 cout<<"文件打开失败目标文件状态可能为只读";
                  system("pause");
                 exit(1);
         }
         myfile<<"Hello"<<endl<<"网址"<<"  @http://990487026.blog.51cto.com "<<endl;
         myfile.close();
 }

 

1基本输入输出

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include "iostream"
using namespace std;

int  main()
{
	char	mybuf[1024];
	int	myInt;
	long	myLong;
	cin >> myInt;
	cin >> myLong;
	cin >> mybuf; // 遇见空格停止接受 数据 
	cout << "int=" << myInt << endl;
	cout << "long=" << myLong << endl;
	cout << "buf=" << mybuf << endl;

	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
123456
987654321
hello world
int=123456
long=987654321
buf=hello
chunli@http://990487026.blog.51cto.com~/c++$



cin.get() 从缓冲区读取输出到屏幕存在IO阻塞

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <stdio.h>
#include "iostream"
using namespace std;

int  main()
{
	char ch;
	while ( (ch=cin.get() )!= EOF )
	{
		cout << ch;
	}
	return 0;
}

ctrl + d结束输入
chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
1
1
2
2
3
3
hello world!
hello world!
chunli@http://990487026.blog.51cto.com~/c++$


cin.get() 如果缓冲区有数据就自动读取

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <stdio.h>
#include "iostream"
using namespace std;

int  main()
{
	char a, b, c;

	cout << "cin.get(a) 如果缓冲区没有数据,则程序阻塞 \n";
	cin.get(a);
	cin.get(b);
	cin.get(c);

	cout << a << b << c << "因为缓冲区有数据,程序不会阻塞\n";
	cin.get(a).get(b).get(c);
	cout << a << b << c << endl;;

	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
cin.get(a) 如果缓冲区没有数据,则程序阻塞 
Hello Linux!
Hel因为缓冲区有数据,程序不会阻塞
lo 
chunli@http://990487026.blog.51cto.com~/c++$



cin.getline 读取一行

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <stdio.h>
#include "iostream"
using namespace std;

int  main()
{
	char buf1[256];
	char buf2[256];
	cout << "请输入一个字符串 含有多个空格 aa bb cc dd\n";
	cin >> buf1;
	cin.getline(buf2, 256);
	cout << "buf1:=>" << buf1 << "buf2:=>" << buf2 << endl; 
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
请输入一个字符串 含有多个空格 aa bb cc dd
aa bb cc dd ee 
buf1:=>aabuf2:=> bb cc dd ee 
chunli@http://990487026.blog.51cto.com~/c++$


cin.ignore函数作用是跳过输入流中n个字符

或在遇到指定的终止字符时提前结束此时跳过包括终止字符在内的若干字符

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <stdio.h>
#include "iostream"
using namespace std;

int  main()
{
	char buf1[256];
	char buf2[256];
	cout << "请输入一个字符串 含有多个空格aa  bbccdd\n";
	cin >> buf1;
	cout << "buf1:" << buf1 << endl;
	
	cin.ignore(3);
	cin.getline(buf2, 256);
	cout << "buf2:" << buf2 << endl; 

	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
请输入一个字符串 含有多个空格aa  bbccdd
abcd
buf1:abcd
abcdefg
buf2:cdefg
chunli@http://990487026.blog.51cto.com~/c++$


cin.peek()查看缓冲区有没有内容

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <stdio.h>
#include "iostream"
using namespace std;

int  main()
{
	cin.ignore(2);
	int myint = cin.peek();
	cout << "ASSIC=" << myint << endl; 

	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
A BCDEFG
ASSIC=66
chunli@http://990487026.blog.51cto.com~/c++$



cin.putback 把收到的字符返回去可以再读一次

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <stdio.h>
#include "iostream"
using namespace std;
int  main()
{
	cout << "Please, enter a number or a word: ";
	char c = std::cin.get();
	if ( (c >= '0') && (c <= '9') ) //输入的整数和字符串 分开处理
	{
		int n; //整数不可能 中间有空格 使用cin >>n
		cin.putback (c);
		cin >> n;
		cout << "You entered a number: " << n << '\n';
	}
	else
	{
		string str;
		cin.putback (c);
		getline (cin, str); 	//字符串 中间可能有空格 使用 cin.getline();
		cout << "You entered a word: " << str << '\n';
	}
	return 0;
}
chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
Please, enter a number or a word: Hello Linux!  
You entered a word: Hello Linux!
chunli@http://990487026.blog.51cto.com~/c++$



标准输出


cout

cout.put()输出单个字符

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <stdio.h>
#include "iostream"
using namespace std;
int  main()
{
	cout << "Hello" << endl;
	cout.put('H').put('E').put('L').put('L').put('O').put('\n');
	return 0;
}
chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
Hello
HELLO
chunli@http://990487026.blog.51cto.com~/c++$


cout.write 标准输出

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <stdio.h>
#include <string.h>
#include "iostream"
using namespace std;
int  main()
{
	const char *p = "http://990487026.blog.51cto.com";
	cout.write(p,strlen(p)) << endl;
	cout.write(p,strlen(p) - 10) << endl;
	cout.write(p,strlen(p) + 5) << endl;
	return 0;
}
chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
http://990487026.blog.51cto.com
http://990487026.blog
http://990487026.blog.51cto.com
chunli@http://990487026.blog.51cto.com~/c++$


cout 的格式化输出 1

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iomanip>
#include <stdio.h>
#include "iostream"
using namespace std;
int  main()
{

	//使用类成员函数
	cout << "<start>";
	cout.width(30);		//设置显示的宽度
	cout.fill('*');		//设置宽度填充
	cout.setf(ios::showbase); //是显示进制  #include <iomanip>
	//cout.setf(ios::internal); //设置数值的符号左右对齐
	cout << hex << 123 << "<End>\n";
	cout << endl;
	//

	//使用控制符
	cout 	<< "<Start>" 
		<< setw(30) 
		<< setfill('*') 
		<< setiosflags(ios::showbase) //基数
		<< setiosflags(ios::internal)
		<< hex
		<< 123
		<< "<End>"
		<< endl;

	return 0;
}
chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
<start>**************************0x7b<End>

<Start>0x**************************7b<End>
chunli@http://990487026.blog.51cto.com~/c++$



cout格式输出2

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iomanip>
#include <stdio.h>
#include "iostream"
using namespace std;
int  main()
{
	int a;
	cout<<"input a:";
	cin>>a;
	cout<<"dec:"<<dec<<a<<endl; //以十进制形式输出整数
	cout<<"hex:"<<hex<<a<<endl; //以十六进制形式输出整数a
	cout<<"oct:"<<setbase(8)<<a<<endl; //以八进制形式输出整数a
	const char *pt="China"; //pt指向字符串"China"
	cout<<setw(10)<<pt<<endl; //指定域宽为,输出字符串
	cout<<setfill('*')<<setw(10)<<pt<<endl; //指定域宽,输出字符串,空白处以'*'填充
	double pi=22.0/7.0; //计算pi值
	//按指数形式输出,8位小数
	cout<<setiosflags(ios::scientific)<<setprecision(8);
	cout<<"pi="<<pi<<endl; //输出pi值
	cout<<"pi="<<setprecision(4)<<pi<<endl; //改为位小数
	cout<<"pi="<<setiosflags(ios::fixed)<<pi<<endl; //改为小数形式输出

	return 0;
}
chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
input a:17
dec:17
hex:11
oct:21
     China
*****China
pi=3.14285714e+00
pi=3.1429e+00
pi=3.143
chunli@http://990487026.blog.51cto.com~/c++$


cout格式输出3

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include <iomanip>
#include <stdio.h>
#include "iostream"
using namespace std;
int  main()
{
	double a=123.456,b=3.14159,c=-3214.67;
	cout<<setiosflags(ios::fixed)<<setiosflags(ios::right)<<setprecision(2);
	cout<<setw(10)<<a<<endl;
	cout<<setw(10)<<b<<endl;
	cout<<setw(10)<<c<<endl;

	return 0;
}
chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
    123.46
      3.14
  -3214.67
chunli@http://990487026.blog.51cto.com~/c++$



文件IO

1文件写入

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include "fstream"
#include "iostream"
using namespace std;

int  main()
{
	const char *fname = "chunli.txt";
	ofstream fout(fname); //建一个 输出流对象 和文件关联;  
	if (!fout)
	{
		cout << "打开文件失败" << endl;
		return -1;
	}
	fout << "hello....111" << endl;
	fout << "hello....222" << endl;
	fout << "hello....333" << endl;
	fout.close();

	return 0;
}
chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
chunli@http://990487026.blog.51cto.com~/c++$ cat chunli.txt 
hello....111
hello....222
hello....333
chunli@http://990487026.blog.51cto.com~/c++$


文件写文件读操作

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include "fstream"
#include "iostream"
using namespace std;

int  main()
{
	const char *fname = "chunli.txt";
	ofstream fout(fname); //建一个 输出流对象 和文件关联;  
	if (!fout)
	{
		cout << "打开文件失败" << endl;
		return -1;
	}
	fout << "hello....111" << endl;
	fout << "hello....222" << endl;
	fout << "hello....333" << endl;
	fout.close();

	//读文件
	ifstream fin(fname); //建立一个输入流对象 和文件关联
	char ch;
	while (fin.get(ch))
	{
		cout <<ch ;
	}
	fin.close();

	return 0;
}
chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
hello....111
hello....222
hello....333
chunli@http://990487026.blog.51cto.com~/c++$


文件追加写入 fname,ios::app

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include "fstream"
#include "iostream"
using namespace std;

int  main()
{
	const char *fname = "chunli.txt";
	ofstream fout(fname,ios::app); //建一个 输出流对象 和文件关联;  
	if (!fout)
	{
		cout << "打开文件失败" << endl;
		return -1;
	}
	fout << "hello....111" << endl;
	fout << "hello....222" << endl;
	fout << "hello....333" << endl;
	fout.close();

	//读文件
	ifstream fin(fname); //建立一个输入流对象 和文件关联
	char ch;
	while (fin.get(ch))
	{
		cout <<ch ;
	}
	fin.close();

	return 0;
}
chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
hello....111
hello....222
hello....333
chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
hello....111
hello....222
hello....333
hello....111
hello....222
hello....333
chunli@http://990487026.blog.51cto.com~/c++$


文件二进制的读写

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include "string.h"
#include "fstream"
#include "iostream"
using namespace std;

class Teacher
{
public:
	Teacher()
	{
		age = 33;
		strcpy(name, "");
	}
	Teacher(int _age, const char *_name)
	{
		age = _age;
		strcpy(name, _name);
	}
	void printT()
	{
		cout << "age=" << age << " name=" << name <<endl;
	}
private:
	int	 age;
	char name[32];
};


int  main()
{

	const char* fname = "chunli.dat";
	ofstream fout(fname, ios::binary); //建一个 输出流对象 和文件关联;  
	if (!fout)
	{
		cout << "打开文件失败" << endl;
		return  -1;
	}
	Teacher t1(31, "C++之父");
	Teacher t2(32, "Linux之父");
	fout.write((char *)&t1, sizeof(Teacher));
	fout.write((char *)&t2, sizeof(Teacher));
	fout.close();

	ifstream fin(fname); //建立一个输入流对象 和文件关联
	Teacher tmp;
	fin.read( (char*)&tmp,sizeof(Teacher) );	tmp.printT();
	fin.read( (char*)&tmp,sizeof(Teacher) );	tmp.printT();
	fin.close();

	return 0;
}
chunli@http://990487026.blog.51cto.com~/c++$ g++  -g -o run main.cpp && ./run 
age=31 name=C++之父
age=32 name=Linux之父
chunli@http://990487026.blog.51cto.com~/c++$




作业练习



1 编程实现以下数据输入/输出

    (1)以左对齐方式输出整数,域宽为12。

    (2)以八进制、十进制、十六进制输入/输出整数。

    (3)实现浮点数的指数格式和定点格式的输入/输出,并指定精度。

    (4)把字符串读入字符型数组变量中,从键盘输入,要求输入串的空格也全部读入,以回车符结束。

    (5)将以上要求用流成员函数和操作符各做一遍。

2编写一程序将两个文件合并成一个文件。

3编写一程序统计一篇英文文章中单词的个数与行数。

4编写一程序将C++源程序每行前加上行号与一个空格。

4.5编写一程序输出 ASCII码值从20到127的ASCII码字符表格式为每行10个。




参考答案

第一题

ios类成员函数实现

#include<iostream>
#include<iomanip>
using namespace std;
int main(){
	long a=234;
	double b=2345.67890;
	char c[100];
	cout.fill('*');
	cout.flags(ios_base::left);
	cout.width(12);
	cout<<a<<endl;
	cout.fill('*');
	cout.flags(ios::right);
	cout.width(12);
	cout<<a<<endl;
	cout.flags(ios.hex);
	cout<<234<<'\t';
	cout.flags(ios.dec);
	cout<<234<<'\t';
	cout.flags(ios.oct);
	cout<<234<<endl;
	cout.flags(ios::scientific);
	cout<<b<<'\t';
	cout.flags(ios::fixed);
	cout<<b<<endl;
	cin.get(c,99);
	cout<<c<<endl;
	return 0;
}
操作符实现
#include<iostream>
#include<iomanip>
using namespace std;
int main(){
	long a=234;
	double b=2345.67890;
	char c[100];
	cout<<setfill('*');
	cout<<left<<setw(12)<<a<<endl;
	cout<<right<<setw(12)<<a<<endl;
	cout<<hex<<a<<'\t'<<dec<<a<<'\t'<<oct<<a<<endl;
	cout<<scientific<<b<<'\t'<<fixed<<b<<endl;
	return 0;
}



第二题

#include<iostream>
#include<fstream>
using namespace std;
int main(){
	int i=1;
	char c[1000];
	ifstream ifile1("D:\\1.cpp");
	ifstream ifile2("D:\\2.cpp");
	ofstream ofile("D:\\3.cpp");
	while(!ifile1.eof()){
		ifile1.getline(c,999);
		ofile<<c<<endl;
	}
	while(!ifile2.eof()){
		ifile2.getline(c,999);
		ofile<<c<<endl;
	}
	ifile1.close();
	ifile2.close();
	ofile.close();
	return 0;
}




第三题

#include<iostream>
#include<fstream>
using namespace std;
bool isalph(char);
int main(){
	ifstream ifile("C:\\daily.doc");
	char text[1000];
	bool inword=false;
	int rows=0,words=0;
	int i;
	while(!ifile.eof()){
		ifile.getline(text,999);
		rows++;
		i=0;
		while(text[i]!=0){
			if(!isalph(text[i]))
				inword=false;
			else if(isalph(text[i]) && inword==false){
				words++;
				inword=true;
			}
			i++;
		}
	}
	cout<<"rows= "<<rows<<endl;
	cout<<"words= "<<words<<endl;
	ifile.close ();
	return 0;
}
bool isalph(char c){
	return ((c>='A' && c<='Z') || (c>='a' && c<='z'));
}


第四题

#include<iostream>
#include<fstream>
using namespace std;
int main(){
	int i=1;
	char c[1000];
	ifstream ifile("D:\\1.cpp");
	ofstream ofile("D:\\2.cpp");
	while(!ifile.eof()){
		ofile<<i++<<": ";
		ifile.getline(c,999);
		ofile<<c<<endl;
	}
	ifile.close();
	ofile.close();
	return 0;
}


第五题

#include<iostream>
using namespace std;
int main(){
	int i,l;
	for(i=32;i<127;i++){
		cout<<char(i)<<" ";
		l++;
		if(l%10==0)cout<<endl;
	}
	cout<<endl;
	return 0;
}