(5)自定义数据结构再探

C++Primer要求自己写一个Salse_data,并引用这个数据类型。这就有一个问题,怎么正确写出一个自己定义的可以引用的头文件(虽然也可以通过在同一个文件中定义来完成这个要求)?于是百度和看了程序案例,发现除了正常的写一个struct外,还要加上#ifndef/#define/#endif,这玩意在定义头文件中有什么用呢?百度到了这篇文章,可以先暂时看这个理解,后面再深究:

 http://blog.csdn.net/abc5382334/article/details/18052757

 

 

这是我自己写的:

//用自己写的类计算卖出的一种书的总销售额、总销售数、平均价格
#include <iostream> 
/*
*后续会详解什么是string类型,现在先说一点我们要用到的:
*string类型其实就是字符的序列,操作有>>、<<、==等,功能有读入、写出字符串和比较字符串
*/
#include <string>
//定义一个Sales_data类型,成员分别记录一种书的ISBN、售出数量、总收入
struct Sales_data {
	std::string bookNo;
	unsigned units_sold = 0;
	double revenue = 0.0;
};
int main()
{
	//定义存储两次交易的数据
	Sales_data data1, data2;
	//定义变量用来记录单次交易的单价
	double price = 0;
	//输入两次交易的所有数据:INSB、售出数量、单次交易的单价,并算出单次交易的总收入
	std::cout << "plase enter The ISBN、amount of sales、unit price twice,end of space bar" << std::endl;
	std::cout << "请分别输入两次交易数据的ISBN、售出数量、单价,空格结束." << std::endl;
	std::cin >> data1.bookNo >> data1.units_sold >> price;
	data1.revenue = data1.units_sold*price;
	std::cin >> data2.bookNo >> data2.units_sold >> price;
	data2.revenue = data2.units_sold*price;
	//如果ISBN号一致,输出ISBN和合并后的总销售数、总收入、平均价格
	if (data1.bookNo == data2.bookNo)
	{
		//总销售数
		unsigned totalCnt = data1.units_sold + data2.units_sold;
		//总收入
		double totalRevenue = data1.revenue + data2.revenue;
		//平均价格
		double avPrice = totalRevenue / totalCnt;
		std::cout << data1.bookNo << " " << totalCnt << " " << totalRevenue << " " << avPrice << std::endl;
	}
	//ISBN不一样,输出错误提示
	else
	{
		std::cout << "error data" << std::endl;
	}
	system("pause");
	return 0;
}

 

书上写的是:

 

#include <iostream> 
#include <string>
//课本上是直接头文件引用Sales_data,这里我改成了在主函数前声明这个类
struct Sales_data {
	std::string bookNo;
	unsigned units_sold = 0;
	double revenue = 0.0;
};
int main()
{
	Sales_data data1, data2;
	double price = 0;
    std::cin >> data1.bookNo >> data1.units_sold >> price;
	data1.revenue = data1.units_sold*price;
	std::cin >> data2.bookNo >> data2.units_sold >> price;
	data2.revenue = data2.units_sold*price;
	//从这里开始和我不一样了,为什么会这样?
	if (data1.bookNo == data2.bookNo)
	{
		unsigned totalCnt = data1.units_sold + data2.units_sold;
		double totalRevenue = data1.revenue + data2.revenue;
		//这行没用std::endl,为什么?
		std::cout << data1.bookNo << " " << totalCnt << " " << totalRevenue << " ";
		if (totalCnt != 0)
			std::cout << totalRevenue / totalCnt << std::endl;
		else
			std::cout << "(no sales)" << std::endl;
		return 0;
	}
	else {
		//cerr、cout、clog的区别?
		std::cerr << "Data must refer to the same ISBN " << std::endl;
		return -1;
	}
	return 0;
}

如上面代码,一共有三个问题需要去解决。

1.我写的为什么和书上的不一样?

书上考虑了会有一本都没卖出去的情况,但我觉得既然都没卖出去,又怎么会输入这个交易数据呢。。。。也许考虑这种bug般的情况是应该的~以后真正写东西的时候要做到“把用户当傻逼”去看待;书上输出错误信息是用cerr而不是cout,为什么会这样后面下面会讨论;书上没有再定义一个变量来存储平均价格,我想了一下,确实不需要再多定义一个变量去存这个数字了,因为没有必要——后面再没有需要使用平均价格的地方了,而totalCnt 、totalRevenue后面都要用到,所以要特别再定义它们来存。

 

2.什么时候用std:endl,什么时候不需要用?这个问题下面会讨论,这里先讨论std::endl和\n的区别。

 首先\n在c里是控制字符,表换行,可作为一个字符来使用;endl是C++中行结束符,只能用于输出流中。(C++继承了c的\n)

其次是endl虽然也有换行的效果,但是它比\n多了一个“刷新”流里的缓冲的flush操作,至于这个操作是什么意思,后面会说到。

 

3.cerr、cout、clog的区别?以及上面提到的什么是刷新缓冲的操作,什么时候要用std::endl。

首先参考这两篇文章:

https://stackoverflow.com/questions/213907/c-stdendl-vs-n

http://blog.csdn.net/garfield2005/article/details/7639833

什么是缓冲区刷新?

缓冲区的目的,就是减少刷屏的次数——比如,你的程序输出一篇文章。不带缓冲的话,就会每写一个字母,就输出一个字母,然后刷屏。有了缓冲,你将看到若干句子“同时”就出现在了屏幕上(由内存翻新到显存)

 

cout是在终端显示器输出,其流通常是传到显示器,但也可以被重定向到文件。cout流在内存中对应开辟了一个缓冲区,用来存放流中的数据,当向cout流插入一个endl,不论缓冲区是否漫了,都立即输出流中所有数据,然后插入一个换行符.

cerr流对象是标准错误流,和cout作用差不多,但流中的信息只能在显示器输出. ,且不经过缓冲区就直接向显示器输出信息

clog流也是标准错误流,作用和cerr一样。但clog中的信息存放在缓冲区,缓冲区满或者遇到endl时才输出.

为什么不把这些功能集中为一体呢?

首先输出错误信息是必须的,cerr之所以选择不经过缓冲区就输出,是怕有些程序bug会导致经过缓冲区的东西无法输出,比如,你的程序遇到调用栈用完了的威胁(无限,没有出口的递归),到什么地方借内存,存放你的错误信息?而如果都和cerr一样不经过缓冲区就输出,就没办法做到若干句子“同时”出现在屏幕上。

也就是说,为了能输出错误信息,不经缓冲区的操作是必须的,于是有了cerr;而为了同时输出信息,经过缓冲区是必须的,于是有了cout。

就是clog让我觉得很奇怪,似乎没有存在的必要.......除非clog是一种能把错误信息重定向到文件的操作,这样它的出现理由就是制作错误文件!现在还不懂怎么重定向到文件,留着以后检验。

 

 

最后是书里的习题解答:

//编写程序,利用自己写的Sales_data,读取两个(或多个)相同ISNBN的销售记录,输出所有记录的和(当输入不同的ISBN数据时结束程序)
#include <iostream> 
#include <string>
struct Sales_data {
	std::string bookNo;
	unsigned units_sold = 0;
	double revenue = 0.0;
};
int main()
{
	Sales_data data1, data2;
	//不能直接std::cin>>data1,可能和Sales_data定义没有Sales_item完整有关
	if (std::cin >> data1.bookNo >> data1.units_sold >> data1.revenue)
	{
		//这里用if是不对的,if只判断一次就结束了,不能多次输入!必须用while循环
		while (std::cin >> data2.bookNo >> data2.units_sold >> data2.revenue)
		{
			if (data1.bookNo == data2.bookNo)
			{
				//一开始以为用不到units_sold和revenue的和,就直接在输出语句里相加了;后来发现要把data1的数据更新才能继续输出其它记录就定义了记录两者的变量totalS和totaR
//“用到的时候再定义,且定义在最靠近第一次使用的地方!”,这就是这句话为什么正确的原因! unsigned totalS= data1.units_sold + data2.units_sold; double StotaR= data1.revenue + data2.revenue; std::cout << data1.bookNo << " " << totalS << " " << StotaR << std::endl; data1.units_sold = totalS; data1.revenue = StotaR; } else { std::clog << "data of no avail" << std::endl; //return是用来终止函数循环的,while和if都不是一个函数所以加了也不能终止程序;break倒是可以终止while这种循环语句(if是判断不是循环)。 //break作用是块作用,比如下面的放在else块里,就意味进入else后整个while就会结束;如果放在while块中就是while只循环一次就结束——这样while就没有意义了! break; } } } return 0; } /*我这个程序有个bug: *我没有定义输入的数据是无效数据的时候会发生什么。 *实际上string能存数字+字母,我想不到有什么数据是无效的,至于后面两个,和double,一旦输入无效数据,就会直接整个程序结束了,没有任何报错。怎么使其报错呢? */

 39~42有疑问(吐槽下,在代码里标红色,实际查看时却并没有变红!)

 

 

//编写程序,读取多条销售记录,并统计每个ISBN有几条销售记录
//这程序有一个bug,如果连续3条或以上ISBN不一样,则输出就要重置了!即真正销售记录要把之前输出过的同ISBN的全部加起来才行。想不到办法解决这个问题了。
#include <iostream> 
#include <string>
struct Sales_data { std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; int main() { Sales_data data1, data2; //这里的data1.units_sold和data1.revenue其实是没有用处的...但为了让用户输入“销售”记录而不是ISBN,所以还是保留了 if (std::cin >> data1.bookNo >> data1.units_sold >> data1.revenue) { //number用来计数,保存同ISBN卖出的数量 //这个number不能写在while里的if里,因为if一旦结束,number这个局部变量就没了,其计数功能就没了 /*同理也不能写在while里,因为一旦while里的if结束number又会被重新定义一次,且我注意到: *int a = 1; *a = a + 1; *int a = ; *这种代码会报错说是重复定义,但是: *while() *int a = 1; *这种就不会!我的理解是while每次执行int a = 1的时候实际上已经把a这个变量抹除了重新定义,所以不算重复定义 *所以要注意,不能随便在循环中定义变量。 */ //因为number在第一个if外不再有什么作用,所以定义在第一个if里和定义在main函数里其实没区别,本着用到时才定义的原则我定义在了第一个if里 int number = data1.units_sold; while (std::cin >> data2.bookNo >> data2.units_sold >> data2.revenue) { //如果ISBN一样,且不是无效数据(如果没至少卖出一本就是无效的),把卖出的书数相加输出 if (data1.bookNo == data2.bookNo && data2.units_sold>0) { number = number + data2.units_sold; std::cout << " we are sold book of " << data2.bookNo << " for " << number << std::endl; } //如果ISBN不一样且卖出至少一本 else if (data1.bookNo != data2.bookNo && data2.units_sold > 0) {
data1.bookNo = data2.bookNo;
number = data2.units_sold;
std::cout << " we are sold book of " << data1.bookNo << " for " << number << std::endl; } //其余情况 else { std::cout << "data is not valid " << std::endl; } } } return 0; } /*what is “if”、“else if”、“else”? *if() *else if() *..... *else if() *else *这段代码的意思是,满足if的就进入if,满足else if的就进入else if,这两种都不满足的就进入else */

24行有疑问

这个程序那几个判断条件我是这样想的:  

  

 

//Sales_data版书店程序,输入多条销售记录,每个记录包括ISBN、售出数、总销售价格,输出同ISBN的售出数、总销售价格、平均销售价格
//这程序有一个bug,如果连续3条或以上ISBN不一样,则输出就要重置了!即真正售出数、总销售价格、平均销售价格要把之前输出过的同ISBN的全部加起来才行。想不到办法解决这个问题了。
//和上个程序基本一样,只不过输出的地方多输出几个数据而已。 #include <iostream> #include <string> struct Sales_data { std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; int main() { Sales_data data1, data2; if (std::cin >> data1.bookNo >> data1.units_sold >> data1.revenue) { //需要totalSold记录销售数,totalRevenue记录总销售额 int totalSold = data1.units_sold;
//注意这里必须用double,不能乱定义一个类型! double totalRevenue = data1.revenue; while (std::cin >> data2.bookNo >> data2.units_sold >> data2.revenue) { //如果ISBN一样,且不是无效数据(如果没至少卖出一本就是无效的),输出售出数、总销售价格、平均销售价格 if (data1.bookNo == data2.bookNo && data2.units_sold>0) { totalSold = totalSold + data2.units_sold; totalRevenue = totalRevenue + data2.revenue; std::cout << " we are sold book of " << data2.bookNo << " for " << totalSold << std::endl; std::cout << " The book fetched " << totalRevenue << " and average price is " << totalRevenue/ totalSold << std::endl; } //如果ISBN不一样且卖出至少一本 else if (data1.bookNo != data2.bookNo && data2.units_sold > 0) {
//重置要放在前面不能放后面,这样就避免了重复输出之前的结果,转而输出当前的结果 data1.bookNo = data2.bookNo; totalSold = data2.units_sold; totalRevenue = data2.revenue; std::cout << " we are sold book of " << data1.bookNo << " for " << totalSold << std::endl; std::cout << "The book fetched " << totalRevenue << " and average price is " << totalRevenue/ totalSold << std::endl; } //其余情况 else { std::cout << "data is not valid " << std::endl; } } } return 0; } //深刻体会到else在if中的强大之处!一旦把必须输出的写到if和else if中后,剩下的统统丢到else里去,一旦出现新的要考虑的东西再加一个else if就好了!

  

  

posted on 2017-10-10 09:23  wuduojia  阅读(343)  评论(0编辑  收藏  举报

导航