九天雁翎的博客
如果你想在软件业获得成功,就使用你知道的最强大的语言,用它解决你知道的最难的问题,并且等待竞争对手的经理做出自甘平庸的选择。 -- Paul Graham

从易到难编写C++程序,(1)个人解答:把键盘输入的字符串逆序输出。

/*Copyright (c) 2007,九天雁翎
* All rights reserved.
* 从易到难编写C++程序,(1)问题:把键盘输入的字符串逆序输出。 
* 完成日期:2007年5月31日
*/
#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iomanip>
using namespace std;
typedef vector<string>::const_iterator c_iter;
int main()
{
 vector<string> svec;    //因为要识别空格,还要换行,光一个string是不行的
 string str;
 cout<<"Please input the string you want to reverse:"<<endl;
    
//开始想以eof为结束,后来后悔了,以'!'为结束,
//开始我用cin作输入,后来后悔了,还是getline好
 while(true)
 {
  getline(cin, str);
  if(str.empty())               //假如输入空格也不应该算错误
  {
   svec.push_back(str);
   continue;
  }
  if(*(str.end() - 1) == '!')    //判断什么时候结束
  {
   str.erase(str.end() - 1);            //去掉结束符号
   reverse(str.begin(), str.end());    //利用标准库的算法逆序str
   svec.push_back(str);
   break;
  }
  reverse(str.begin(), str.end());    //利用标准库的算法逆序str
  svec.push_back(str);
 }//end of while
 reverse(svec.begin(), svec.end());               //这一步可以在下一步用反向迭代器替换
 for(c_iter it = svec.begin();            //输出svec
  it != svec.end();++it)
 {
  cout<<*it<<endl;
 }
 return 0;
}
 
//这是我个人见过最笨的方法,仅仅作为一种个人学习的历史保留,勿学,好的解答参考同题解答(2)

阅读全文....

突然决定,再发文章,只讲实例,思想,不讲细节

也许思想才是最重要的,搞那么多细节有什么用?翻翻书不就懂了,以前常常假设,假如要我从零基础来讲C++,我会怎么来讲,还是按部就班的如TC++PL,C++ Primer,一样吗?以前感觉好像也没有别的办法,我现在突然觉得,第一节课,我就会告诉我的学生,买两本书TC++PL,C++ Primer,嫌贵?首先不说没有钱学编程比较难,去下电子版吧。然后大概讲下编程的概念,就开始要学生实现一些实际的功能与编写实用的,他们自己感兴趣的例子,为什么不呢?碰到不懂的东西,书上可以查啊?不会查书?那你还学什么编程?你以为能学到什么地步可以编程序不查书吗?没有那一天!那么现在就开始习惯吧。也许对于我这样学习C才几年,学习C++也才几个月的人,就开始讨论准备怎么教学生有点可笑,但是,我一直以为,教与学是互相促进的,看过最深刻的话是,碰到比较难的概念,就尝试向别人讲清楚,然后自己才能清楚,而且,我们校长讲选老师的时候这样说的,,学术水平非常高,学术素养过人,而且讲课讲得有深度能浅出,学生喜欢,这是教学水平好,而没有什么学术水平,但是讲课却能虎虎生风,面面俱到,深入主旨,要点,那叫教学艺术!斯蒂文霍金以前讲他刚刚到三一学院教数学的时候,还是自己前一天看一课,然后第二天教一课的呢,我怕什么?又不误人子弟,网上随便发发而已:)

阅读全文....

关于C++学习的再思考

今天看过一篇文章以后,原文(http://www.shubulo.com/viewthread.php?tid=32828&highlight=) 才发现自己其实也走上了作者所谓的歪路。的确,C++有太多太多的细节了,很容易让人深入其中,于是,大家都有了大量的热情投入其中,以至于以为不深究其细节就不能懂得怎么编好一个程序,我突然想起什么时候看过某个C牛人描述以前他刚开始用C语言的时候,是因为用另一种语言解决问题碰到了障碍,然后别人塞给他一本TCPL,于是他编敲代码,边查看此书,最后完成了任务,他还感叹,这就是我需要的语言!现在呢?我似乎准备先啃C++ Primer后啃TC++PL,然后Effective C++和More Effective C++,Effective STL,同时还准备彻底重补数学,分析数学,离散数学等等等等,然后再开始自己的编程之旅,似乎没有看完这些书,我就不能用C++来解决任何问题。是的,我的确是被一种社团的氛围所引导,看多了怎么学C++的前人的教训,他们不就是这样说的吗?想起以前,刚学C++的时候,类是什么都还不了解,我用VC的引导+自己原来的C语言基础+不懂就查看MSDN,编出了我自己当时想要的学习档案管理器,还有一个画图程序,我当时可是什么C++的书都没有看完,就编出了Windows程序,现在回头看,一个是用了.Net Framework,一个是用了MFC,而现在的我却准备学完几十本C++的书再看一堆数据结构的书,算法的书,如编程艺术,算法导论以后,然后再从Programming Windows开始,然后深入浅出MFC,MFC编程.......然后再开始编我自己的程序!这不是太奇怪了吗?难道我要使用一个剪刀,一定要先化学开始学习,分析清楚了剪刀的化学成分后,再开始学习物理,分析用力的情况,然后因为数学不够好,物理学习不能深入,开始饿补数学,然后再开始学习怎么使用剪刀吗?C++也仅仅是一个工具而已!天哪,我真的不知道自己是怎么走上这一条奇怪的道路的,需要用的时候再去学习,不要去学自己不需要的东西,也许在其他地方不适用,但是在编程方面却是很适用的,因为技术更新如此之快,你不可能一下子学完所有的东西,但你把你计划学的东西都学了,你会发现很多东西还在你的计划之外............大家讨论了太多基础要好,不然不能编写出健壮的,·#¥#¥……¥%……—%¥— 的程序,没有错,不过,基础一定是要在一开始就打好吧?建房子也许需要,不过编程序是建房子吗?不是!过程中慢慢可以学习,很多已经很好的程序员回过头来,说自己走了很多弯路,因为以前的程序编的不够好,因为基础不扎实,于是开始总结经验,学弟学妹啊,一定不要走我以前走的弯路啊,你没有学 什么什么一定不要怎么怎么的,那也许真是心里话,不过一定是正确的吗?我现在开始怀疑了........................................也许我现在开始尝试有意义的编程,我还不是个高效率的程序员,不能编写健壮的程序,但是我现在还不开始编写,那我也许永远也不会是个程序员!

阅读全文....

从易到难编写C++程序,(1)问题:把键盘输入的字符串逆序输出。

        先为整个系列来个说明,我不算个C++的高手,哪怕入门都还算不上,但是我却愿意给还没有开始学习C++的人一个指引,也许我不配,但是你可以选择不看啊。我只说两句话,1、自己编程是学习C++最佳的方式;2、当需要一个东西的时候,才去了解它。对不对,我不知道,但我这样认为,你也可以有你自己的看法。我每次提出一个问题,然后想办法解答,我通过这些我自己提出的问题进一步的学习。为了方便,问题一个帖,解答一个帖,因为解答有可能是在提出问题很多天后....................你看到了也可以先尝试。看到我的解答有什么不对,或者有什么可以改进的,欢迎你提出来。我用的编译器是VC.NET 2005。so......................first,光Hello World也太没有意思了,简单的,把键盘输入的字符串逆序输出。 

阅读全文....

收到TCPL的时候,还是感叹C语言的简洁

 当我的电脑桌前摆着800多面的TC++PL的和近800面的TC++SL,并且正在学习700多面的C++ Primer的时候,当我收到一共只有250面而实际内容讲解只有将近150面的TCPL(后面为UNIX接口,C标准文档解释及附录)的时候我还能有什么想法?而且我还知道,C可以解决任何C++能解决的问题,虽然说要进行C语言的实际开发,光这150面的内容还不够,还需要参考书,但是,难道要进行C++的实际开发,TC++PL,TC++SL,C++ Primer又够了吗?天哪,150面的C啊。。。。。。。。。除了没有想法,还是没有想法。虽然我知道我无异于在学会了VC.net 2005后感叹TC2.0的简洁。。。。。但是简洁就是简洁。。。。。

阅读全文....

学了模板再来看容器输出的简化

以前提到了在学习C++标准库的过程中《 关于容器输出的学习与简化过程 》,当时没有学习一点模版的东西,所以怎么弄都还是比较复杂,学到模版的第一件事,我就是想用它来简化容器的输出,当然,实现后,我也体会到了模版的强大和泛型编程方式的优点(这样说似乎是太大了,不过可以管中窥豹嘛),下面看一个用模版实现的容器输出:

template <class T>
void printCon(const T &orig)
{
 for(T::const_iterator it = orig.begin();it != orig.end();++it)
  cout<<*it<<" ";
 cout<<endl;
}

简单几行,就可以输出一切容器。。。。而且还包括你自己实现的带类似成员的自定义类,其功能强大,不言而喻!难怪在看到C++的模版技术后,JAVA也引进了这一强大技术(似乎是在JDK1.5以后)。这样就太短了,可是也没有什么其它好说的了。看看使用吧。

#include<iostream>
#include<vector>
#include<string>
using namespace std;
int main()
{
vector<string> svec;
svec.push_back("ABC");
svec.push_back("BCD");
printCon(svec);
vector<int> ivec;
ivec.push_back(100);
ivec.push_back(10);
printCon(ivec);
}

一经定义,反复使用:)简直就是一劳永逸嘛。。。。虽然话是说C能实现C++的一切功能,但是。。。。要实现这样的容器输出该需要多少行代码啊。。。。。(#·¥%#……#¥……,C中根本就没有这样的容器,不需要实现这样的输出.......................晕)

阅读全文....

浅谈C++类(10)--函数对象

欢迎转载,但请标明作者 “九天雁翎”,当然,你给出这个帖子的链接更好。 

好久好久没有时间学C++了,郁闷,主要是因为最近有考试逼近,不得不看一些其他的书。看那些不能说没有用处,但是实在是不怎么感兴趣的书,也是一种痛苦。今天很晚了,抽出点时间,学了个很有用的东--函数对象,的确很有用。关于操作符重载其实并没有讲完,比如前++,后++,*,[]等等但是都差不多,感觉没有什么好讲的,我个人对这个浅谈系列的定位感觉应该是一些自己的笔记和心得,并不像让它成为百科,因为以前太过于求全,浪费了太多时间,以后碰到想讲的就讲,不想讲的就不为了全而凑数了。回到主题,先看一个我没有讲过的操作符重载(),在一个家伙后面加括号,那不就是函数吗?恩,就让类可以像函数一样调用!呵呵,搞了这么久,类才有点新意。看个没有函数对象的例子先,还是我们的水果,当然为了简单,我把以前那些复杂的东西都删了,就是个简单的水果。

例10.0:

 

#include <string>
#include 
<iostream>
using namespace std;
class Fruit               //定义一个类,名字叫Fruit
{
    
string colour;   //定义一个colour成员表示颜色
    string name;     //定义一个name成员表示名字  
    double weight;   //定义一个weight成员表示重量
public:
    Fruit(
const string &cst = "green",const string &nst = "apple",const double &dw = 0.0):colour(cst),name(nst),weight(dw)//构造函数
    {
    }

    
~Fruit()   //析构函数不需要定义,用系统的就好了
    {
    }
  
    
bool equalName(const Fruit &orign)
    
{
        
return name == orign.name;
    }

    
bool equalColour(const Fruit &origc)
    
{
        
return colour == origc.colour;
    }

    
bool equalWeight(const Fruit &origw)
    
{
        
return weight == origw.weight;
    }

}
;



int main()
{
    Fruit greenApple;
    Fruit redApple(
"red");
    Fruit blueApple(
"blue","apple",1.0);
    cout
<<greenApple.equalName(redApple)<<endl;
    cout
<<greenApple.equalName(blueApple)<<endl;
    cout
<<redApple.equalWeight(blueApple)<<endl;
    
    
return 0;
}

 

不要抱怨我以前的程序怎么没有缩进,其实我缩进了,不过复制过来就没有了,现在才知道可以插入代码的方式插入,然后看起来好像是好一些了。这个程序无非就是定义了3个成员函数,然后测试了下是否正常工作。现在我们从一个奇怪的出发点来考虑一下,假如现在需要调用equalName很多次,每次都要这样调用嫌麻烦了一点,怎么办?呵呵,把equalName()改名为e(),自然 不错,你还要一个小点和e呢,假如用成员函数的话,都省略了,当然,在这种情况下,意义有点含糊了。不过简便还是简便了。看例10.1:

例10.1:

#include <string>
#include 
<iostream>
#include 
<algorithm>
#include 
<vector>
using namespace std;
class Fruit               //定义一个类,名字叫Fruit
{
    
string colour;   //定义一个colour成员表示颜色
    string name;     //定义一个name成员表示名字  
    double weight;   //定义一个weight成员表示重量
public:
    Fruit(
const string &cst = "green",const string &nst = "apple",const double &dw = 0.0):colour(cst),name(nst),weight(dw)//构造函数
    {
    }

    
~Fruit()   //析构函数不需要定义,用系统的就好了
    {
    }
  
    
bool equalName(const Fruit &orign)
    
{
        
return name == orign.name;
    }

    
bool equalColour(const Fruit &origc)
    
{
        
return colour == origc.colour;
    }

    
bool equalWeight(const Fruit &origw)
    
{
        
return weight == origw.weight;
    }

    
bool operator()(const Fruit &orig)    //重载了()操作符
    {
        
return equalName(orig);
    }

}
;



int main()
{
    Fruit greenApple;
    Fruit redApple(
"red");
    Fruit blueApple(
"blue","apple",1.0);
    Fruit redOrange(
"red","orange");
    Fruit yellowOrange(
"yellow","orange");
    cout
<<greenApple(redApple)<<endl;   //假设这个要调用很多次:)还是很方便的
    return 0;
}

 

用处可不只这一点啊,在一些标准库中的应用更是方便,见下例:

例10.2:

 

#include <string>
#include 
<iostream>
#include 
<algorithm>
#include 
<vector>
using namespace std;
class Fruit               //定义一个类,名字叫Fruit
{
    
string colour;   //定义一个colour成员表示颜色
    string name;     //定义一个name成员表示名字  
    double weight;   //定义一个weight成员表示重量
public:
    Fruit(
const string &cst = "green",const string &nst = "apple",const double &dw = 0.0):colour(cst),name(nst),weight(dw)//构造函数
    {
    }

    
~Fruit()   //析构函数不需要定义,用系统的就好了
    {
    }
  
    
bool equalName(const Fruit &orign)
    
{
        
return name == orign.name;
    }

    
bool equalColour(const Fruit &origc)
    
{
        
return colour == origc.colour;
    }

    
bool equalWeight(const Fruit &origw)
    
{
        
return weight == origw.weight;
    }

    
bool operator()(const Fruit &orig)
    
{
        
return equalName(orig);
    }

}
;



int main()
{
    Fruit greenApple;
    Fruit redApple(
"red");
    Fruit blueApple(
"blue","apple",1.0);
    Fruit redOrange(
"red","orange");
    Fruit yellowOrange(
"yellow","orange");
    vector
<Fruit> aveF;
    aveF.push_back(greenApple);
    aveF.push_back(redApple);
    aveF.push_back(blueApple);
    aveF.push_back(redOrange);
    aveF.push_back(yellowOrange);
    cout
<<count_if(aveF.begin(),aveF.end(),greenApple)<<endl;//第一种调用方式
    cout<<count_if(aveF.begin(),aveF.end(),Fruit("green","orange"))<<endl;//第二种方式,创建一个临时对象
    return 0;
}

 

是不是同意我的观点了?很有用吧。因为太有用了,标准库也定义了一批函数对象模板给我们用,详细的我一下肯定也说不完,也超出本文范围了,查查资料吧。还有个函数对象适配器,也很有意思。看个例子。

例10.3:

 

#include <string>
#include 
<iostream>
#include 
<algorithm>
#include 
<vector>
#include 
<functional>
using namespace std;
class Fruit               //定义一个类,名字叫Fruit
{
    
string colour;   //定义一个colour成员表示颜色
    string name;     //定义一个name成员表示名字  
    double weight;   //定义一个weight成员表示重量
public:
    Fruit(
const string &cst = "green",const string &nst = "apple",const double &dw = 0.0):colour(cst),name(nst),weight(dw)//构造函数
    {
    }

    
~Fruit()   //析构函数不需要定义,用系统的就好了
    {
    }
  
    friend 
bool operator==(const Fruit&,const Fruit&);
}
;
bool operator==(const Fruit &lf,const Fruit &rf)  //重载一个==操作符表示名字相等
{
    
return lf.name == rf.name;
}



int main()
{
    Fruit greenApple;
    Fruit redApple(
"red");
    Fruit blueApple(
"blue","apple",1.0);
    Fruit redOrange(
"red","orange");
    Fruit yellowOrange(
"yellow","orange");
    vector
<Fruit> aveF;
    aveF.push_back(greenApple);
    aveF.push_back(redApple);
    aveF.push_back(blueApple);
    aveF.push_back(redOrange);
    aveF.push_back(yellowOrange);
    cout
<<count_if(aveF.begin(),aveF.end(), bind2nd ( equal_to<Fruit>() , Fruit("green","apple")))<<endl;
    cout
<<count_if(aveF.begin(),aveF.end(), bind2nd ( equal_to<Fruit>() , Fruit("green","orange")))<<endl;
    cout
<<count_if(aveF.begin(),aveF.end(),not1 ( bind2nd ( equal_to<Fruit>() , Fruit("green","apple"))))<<endl;
    cout
<<count_if(aveF.begin(),aveF.end(),not1 ( bind2nd ( equal_to<Fruit>() , Fruit("green","orange"))))<<endl;
    
return 0;
}

 

这个程序其他部分都很简单,主要就是最后那几行比较难理解,好像也很复杂,恩,是很复杂,但是当你了解他实现的更复杂的功能的时候,你会发现,那其实是多么的简单。我只讲最后一行,其他自己理解:)Fruit("green","orange")表示建立一个名为orange的临时Fruit对象,equal_to<Fruit>()就是我说的标准库里面的一个函数对象模板,它对()内的2个操作数调用==操作符并返回bool值,这里似乎他没有参数,呵呵,因为bind2nd表示绑定一个值给2元函数的第2个实参,相应的bind1st表示绑定给第一个,他们自己都是接受两个2个参数,第一个为一个函数对象,第二个为你要绑定的值,not1表示对一个1元操作取反。count_if我就不说了,也就是说,最后一句输出表示输出aveF中名字不为orange的对象的数目。呵呵,功能强大:)想想没有这些功能你需要怎么去实现吧,然后把数目扩大。。。。。。

阅读全文....

浅谈C++类(9)--重载算数关系操作符

欢迎转载,但请标明作者 “九天雁翎”,当然,你给出这个帖子的链接更好。 

本来 是可以一讲就把重载全部讲完的,因为昨天太晚了,很困,所以就只讲了重载输入输出操作符,今天概念性的东西就不说了,直接看上一讲的《浅谈C++类(8)--重载输入输出操作符 》吧,今天就补充一下其他的操作符的重载,其实都差不多,不过我感觉自己实际输入调试过后和没有调试只懂概念有个印象是完全不一样的。我一次把除了下标和成员访问操作符以外的操作符都写在下面这个例子里面,你自己分析和调试吧,我在主程序里面只调试了一部分。

例9.0:

#include <string>
#include <iostream>
using namespace std;
class Fruit               //定义一个类,名字叫Fruit
{
 double price;    //定义一个price成员表示价格
 double weight;   //定义一个weight成员表示重量
 string colour;   //定义一个colour成员表示颜色
 string name;     //定义一个name成员表示名字   
 double conValue;  //定义一个conValue成员表示总价值
public:
 friend istream& operator>>(istream&,Fruit&);     //重载输入操作符
 friend ostream& operator<<(ostream&,const Fruit&);  //重载输出操作符
 Fruit& operator+=(const Fruit &orig)   //重载复合加法操作符,行为不知道怎么定义好,所以比较奇怪
 {
  if(name != orig.name)
  {
   name = name + " mix " + orig.name;
  }
  if(colour != orig.colour)
  {
   colour = colour + " mix " + orig.colour;
  }
  weight += orig.weight;
  conValue += orig.conValue;  
  price = conValue / weight;
  return *this;
 }
 friend Fruit operator+(const Fruit &lf,const Fruit &rf);  //重载加法操作符,必须要用友元,因为有两个操作数
 Fruit& operator=(Fruit &orig)   //重载赋值操作符
 {
  name = orig.name;
  colour = orig.colour;
  price = orig.price;
  weight = orig.weight;
  conValue = orig.conValue;
  return *this;
 }
 bool operator==(const Fruit &orig)//重载相等操作符
 {
  return conValue == orig.conValue;
 }
 bool operator!=(const Fruit &orig)//重载不等操作符
 {
  return !(*this == orig);
 }
 bool operator<(const Fruit &orig)  //重载小于操作符
 {
  return conValue < orig.conValue;
 }
 bool operator>(const Fruit &orig)   //重载大于操作符
 {
  return conValue > orig.conValue;
 }
 bool operator<=(const Fruit &orig)  //重载小于等于操作符
 {
  return *this<orig || *this==orig;
 }
 bool operator>=(const Fruit &orig)  //重载大于等于操作符
 {
  return *this>orig || *this==orig;
 }
 void print()              //定义一个输出的成员print()
 {
  cout<<weight<<" kilogram "<<colour<<" "<<name
   <<" worth "<<conValue<<" yuan."<<endl;
 }
 Fruit(const double &dp,const double &dw,const string &cst = "green",/
    const string &nst = "apple"):price(dp),weight(dw),colour(cst),name(nst)//构造函数
 {
 conValue = price * weight;
 }
 Fruit(const Fruit &orig)   //定义一个复制构造函数
 {
  name = orig.name;
  colour = orig.colour;
  price = orig.price;
  weight = orig.weight;
  conValue = orig.conValue;
 }
 ~Fruit()   //析构函数不需要定义,用系统的就好了
 {
 } 
};
ostream& operator<<(ostream &out,const Fruit &s)
{
 cout<<s.weight<<" kilogram "<<s.colour<<" "<<s.name
  <<" worth "<<s.conValue<<" yuan.";
 return out;
}
istream& operator>>(istream& in,Fruit &s) 
{
 cout<<"price:";
 in>>s.price;
 cout<<"weight:";
 in>>s.weight;
 cout<<"what:";
 in>>s.colour>>s.name;
 s.conValue=s.price*s.weight;
 if(!in)
  cerr<<"Wrong input!"<<endl;
 return in;
}
Fruit operator+(const Fruit &lf,const Fruit &rf)
{
 Fruit ret(lf);
 ret += rf;
 return ret;
}

int main()
{
 Fruit greenApple(3.0,10.0);
 Fruit redApple(4.5,10.0,"red");
 Fruit mixApple = greenApple + redApple;
 if(greenApple == redApple)
 {
  cout<<"They are the same: ";
 }
 if(greenApple != redApple)      
 {
  if(greenApple > redApple)
   cout<<"biger: "<<greenApple<<endl;
  if(greenApple < redApple)
   cout<<"biger: "<<redApple<<endl;
 }
 cout<<greenApple<<endl;
 cout<<redApple<<endl;
 cout<<"mix: "<<mixApple<<endl;
 
 return 0;
}

程序很简单,注释也够多了,不多解释了,有些建议是要说的,两个形参的操作符就用友元函数吧,因为成员函数是有个隐藏形参this的,操作符之间可以互相调用以简化,比如operator+就用了operator+=,operator>=就用了operator>等,赋值操作记得返回对*this的引用,以方便连续使用比如a=b=c或者d+=e+=f。

假如你觉得a=b=c的形式还比较好理解,但是对d+=e+=f的形式很陌生,不知道怎么回事的话,我给出个例子。

例9.1:

#include <iostream>
using namespace std;
int main()
{
 int a=1;
 int b=3;
 int c=4;
 int d=6;
 d+=a+=b+=c;
 cout<<"a: "<<a<<endl
  <<"b: "<<b<<endl
  <<"c: "<<c<<endl
  <<"d: "<<d<<endl;
 return 0;
}

你看到结果就会明白,+=操作符是从右读过来的,我们为什么要返回*this就很好理解了,我们实际就是返回左操作数。

阅读全文....

浅谈C++类(8)--重载输入输出操作符

欢迎转载,但请标明作者 “九天雁翎”,当然,你给出这个帖子的链接更好。

其实我们已经用过操作符重载,还记得<<和>>吗?本来不是移位操作符吗?在C++里面我们已经把他们当作输入输出操作符用过了,我们今天来研究一下重载他们用来输入输出类,先还是用水果来举一个例子。

例8.0:

#include <string>
#include <iostream>
using namespace std;
class Fruit               //定义一个类,名字叫Fruit
{
 string name;     //定义一个name成员          
 string colour;   //定义一个colour成员
public:
 friend istream& operator>>(istream&,Fruit&);        //必须要声明为友元啊,不然怎么输入啊
 friend ostream& operator<<(ostream&,const Fruit&);       //同理
 void print()              //定义一个输出名字的成员print()
 {
  cout<<colour<<" "<<name<<endl;
 }
 Fruit(const string &nst = "apple",const string &cst = "green"):name(nst),colour(cst)
 {
 }  //构造函数
 ~Fruit()
 {
 } 
};
ostream& operator<<(ostream &out,const Fruit &s)           //我是输出操作符的重载
{
 out<<s.colour<<" "<<s.name;
 return out;
}
istream& operator>>(istream& in,Fruit &s)           //我是输入操作符的重载
{
 in>>s.colour>>s.name;
 if(!in)
  cerr<<"Wrong input!"<<endl;
 return in;
}
int main()
{
 Fruit apple;
 cin >>apple;
 cout<<apple;
 
 return 0;
}

对照着例子开始说明一下,重载这个词以前是用在函数上面的,而实际上C++中好像也把操作符看作一种特殊的函数,特殊的方面仅仅是在函数名是操作符而已,其他和函数没有什么区别,当作函数来对待就好了,函数无非就是 返回值 函数名(参数)的形式,重载操作符的时候也是这样,为了方便说明是操作数作函数名,这里用的是operator后接操作符的形式,如本例中说明的是输入输出操作符,就是operator<<,operator>>,这样,这个例子也许你还看不出用重载输入输出有什么好,我很久前就定义了一个print() 成员函数,以前不是都很好的完成了输出任务吗?而用构造函数也可以很好的完成输入了。当然说是这样说,但是操作符的特点是简单明了,而C/C++追求的就是简洁,当年C程序为了简洁甚至让一切东西默认int呢,要得就是简洁。比如,当大量的输出需要处理的时候,我们用函数就要这样,apple1.print();apple2.print();apple3.print()......................但是用操作符的话就可以这样,cout<<apple1<<apple2<<apple3;哪个简洁自然非常明了。所以虽然我们不用操作符好像也可以完成任务,不过我们还是偏向于使用重载操作符的方式,比如plus(a,b)我们自然愿意用a+b,我们还用"!"来取代empty()用"=="来取代equal(),用"+="取代a=plus(a,b),等等等等。

阅读全文....

当n非常非常大时(比如为1万),求n!问题的学习,解答和疑问。

 

欢迎转载,但请标明作者 “九天雁翎”,当然,你给出这个帖子的链接更好。

首先说明,这本来是钱能C++程序设计习题及解答中的一个习题。也就是在自然数范围内求n!,其实用他的方法还不能说在自然树范围内,按他的方法,不考虑机子性能,最多可以算到2^32-1(32位机),原题解答如下:

程序1:

#include <iostream>
#include <iomanip>
#include <cmath>
#include <cstdlib>
using namespace std;
//--------------------------------------------------------------------
int getN();
int getBitNum(int n);
char *init(int size);
void calc(char *a,int n);
void display(char *a,int size);
//--------------------------------------------------------------------
int main()
{
 int n = getN();
 int size = getBitNum(n);
 char *pa = init(size);
 calc(pa,n);
 display(pa,size);
 delete []pa;
 return 0;
}
//----------------------------------------------------------------------
int getN()
{
 int n;
 cout <<"请输入n!中的n:";
 cin >>n;
 while(n<0)
 {
  cout <<"输入有错,请重输:";
  cin >>n;
 }
 if(n == 0)
  exit(1);
 return n;
}
//--------------------------------------------------------------------
int getBitNum(int n)
{
 double sum = 1.0;
 for(int i = 1;i <= n;++i)
  sum += log10(double(i));
 return int(sum);
}
//--------------------------------------------------------------------
char *init(int size)
{
 char *pa = new char[size];
 if(!pa)
 {
  cout<<"Too large factor of"<<size<<endl;
  exit(1);
 }
 pa[0] = 1;
 for(int i = 1;i <size;++i)
  pa[i] = 0;
 return pa;
}
//--------------------------------------------------------------------
void calc(char *a,int n)
{
 double bitcount = 1;
 int begin = 0;
 for(int i = 2;i <= n;++i)
 {
  long and = 0;
  bitcount += log10(double(i));
  if(a[begin] == 0)
   ++begin;
  for(int j = begin;j < int(bitcount); ++j)
  {
   and += i * a[j];
   a[j] = char(and % 10);
   and /= 10;
  }
 }
}
//--------------------------------------------------------------------
void display(char *a,int size)
{
 int bit = 0;
 for(int i = size - 1;i >= 0; --i)
 {
  if(bit % 50 == 0)
   cout <<endl<<"第"<<setw(3)<<(bit/50+1)<<"个50位:";
  cout <<(int)a[i];
  ++bit;
 }
 cout <<endl;
}
//--------------------------------------------------------------------

程序的运行效率和结构化设计都很不错,这里赞一句清华大学钱能教授的C语言编程水平,注意,仅仅是佩服他的C语言编程水平而已,这根本就不能算是一个好的C++程序,虽然他是在一个C++教程里面给出来的。我把自己按他的算法重新编写了代码,没有他那么好看了,但是因为int明显是正的,所以用了unsigned类型的size_t,这样就让n可接受的范围到了2^32-1。

程序2:

#include <iostream>
#include <iomanip>
#include <cmath>
#include <cstdlib>
using namespace std;

size_t getbit(const size_t &aN);
void init(unsigned char *p,size_t &am);
void compute(unsigned char *p,const size_t &an);
void print(unsigned char *p,const size_t &am);
int main()
{
 cout <<"Input the /"n/" you want to compute:";
 size_t n;
 cin >> n;
 if(n == 0)           //检验输入
 {
  cerr<<"Please input a integer more than zero."<<endl;
 }
 size_t m = getbit(n);            
 unsigned char *pn = new unsigned char[m];
 if(!pn)            //检验空间分配
 {
  cerr<<"The factor is too big."<<endl;
 }
 init(pn,m);        //初始化
 compute(pn,n);   //计算n!
 print(pn,m);         //输出
 delete []pn;
 return 0;
}
//-----------------------------------------------------------
size_t getbit(const size_t &aN)
{
 double sum = 1.0;
 for(size_t i = 1;i<=aN;++i)
 {
  sum+=log10(double(i));
 }
 return size_t(sum);
}
//------------------------------------------------------------
void init(unsigned char *p,size_t &am)
{
 p[0]=1;
 for(size_t i = 1;i != am;++i)
 {
  p[i] = 0;
 }
}
//------------------------------------------------------------
void compute(unsigned char *p,const size_t &an)
{
 double bitcount = 1.1;
 size_t begin = 0;
 for(size_t i = 2;i<=an;++i)
 {
  size_t and = 0;
  bitcount +=log10(double(i));
  if(p[begin]==0)
   ++begin;
  for(size_t j = begin ; j < size_t(bitcount);++j)
  {
   and += i * p[j];
   p[j] = unsigned char(and % 10);
   and /= 10;
  }
 }
}
//----------------------------------------------------------------
void print(unsigned char *p,const size_t &am)
{
  size_t bit = 0;
 for(size_t i = am;i != 0;--i)
 {
  if(bit % 50 == 0)
   cout <<endl<<"第"<<setw(3)<<(bit/50+1)<<"个50位:";
  cout << size_t(p[i-1]);
  ++bit;
 }
 cout<<endl;
}

我这里几乎都继承了钱能的算法,哪怕结构都差不多,仅仅是改变成了unsigned,可是竟然有问题,在运行时,当n比较小时(如50),有的时候会出错,大的时候反而不会(比如1000,2000),不知道是为什么。为了方便以后的使用,并体现一点C++的精神,我把这个程序作成一个类,就变成下面这样.

程序3:

Factor.h:

#pragma once
typedef  unsigned char unchar;
class Factor
{
size_t n;             //求n的n!
size_t m;             //n!有多少位
unchar *pc;       //动态分配的数组指针
private:
 void getbit();
 void compute();   //计算n!
 void init();
 Factor(Factor &);
public:
 void print()const;         //输出
 Factor(const size_t &);
 ~Factor(void);
 
};

Factor.cpp

#include "Factor.h"
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;

Factor::Factor(const size_t &nval)
{
 if(nval == 0)           //检验输入
 {
 cerr<<"Please input a integer more than zero."<<endl;
 }
 n = nval;
 init();
}
Factor::~Factor()
{
 delete []pc;
}
void Factor::init()
{
 getbit();            
 pc = new unchar[m];
 if(!pc)            //检验空间分配
 {
  cerr<<"The factor is too big."<<endl;
 }
 pc[0]=1;
 for(size_t i = 1;i < m;++i)
 {
  pc[i] = 0;
 }
 compute();
}

void Factor::getbit()
{
 double sum = 1.0;
 for(size_t i = 1;i<=n;++i)
 {
  sum+=log10(double(i));
 }
 m = size_t(sum);
}
//------------------------------------------------------------
void Factor::compute()
{
 double bitcount = 1.1;
 size_t begin = 0;
 for(size_t i = 2;i<=n;++i)
 {
  size_t and = 0;
  bitcount +=log10(double(i));
  if(pc[begin]==0)
   ++begin;
  for(size_t j = begin ; j < size_t(bitcount);++j)
  {
   and += i * pc[j];
   pc[j] = unchar(and % 10);
   and /= 10;
  }
 }
}
//----------------------------------------------------------------
void Factor::print()const
{
  size_t bit = 0;
 for(size_t i = m;i != 0;--i)
 {
  if(bit % 50 == 0)
   cout <<endl<<"第"<<setw(3)<<(bit/50+1)<<"个50位:";
  cout << size_t(pc[i-1]);
  ++bit;
 }
 cout<<endl;
}

 

main.cpp

#include "Factor.h"
#include <iostream>
using namespace std;
int main()
{
 size_t anum;
 cout<<"Input the number you want to compute:";
 cin >>anum;
 Factor a(anum);
 cout<<"the "<<anum<<"! is:"<<endl;
 a.print();
 return 0;
}

主程序仅仅是用来测试,这个类可以比较好的计算n!,问题是还是有些类似程序2的问题,我也是很困惑,难道是因为用了无符号的问题?因为这2个程序和原题答案这是个最大的区别,其他几乎一样。然后我为了用上C++的STL特性,编写了如下代码:

程序4:

Factor.h:

#pragma once
#include <vector>
class Factor
{
size_t n;             //求n的n!
std::vector<size_t> vec;       //储存结果的vector
private:
 void compute();   //计算n!
 void init();
 Factor(Factor &);
public:
 void print();         //输出
 Factor(const size_t &);
 ~Factor(void);
};

Factor.cpp

#include "Factor.h"
#include <iostream>
#include <cmath>
#include <cmath>
#include <iomanip>
using namespace std;
typedef vector<size_t>::iterator iter;

Factor::Factor(const size_t &nval)
{
 if(nval == 0)           //检验输入
 {
 cerr<<"Please input a integer more than zero."<<endl;
 }
 n = nval;
 compute();
}
Factor::~Factor()
{
}
//------------------------------------------------------------
void Factor::compute()            //这个最主要的函数有问题
{
 vec.push_back(1);
 for(size_t i = 2;i<=n;++i)
 {
  size_t and = 0;
  for(iter it = vec.begin();it != vec.end();)      //每次都计算了end(),但是还是出错,不知道为什么
  {
   and += i * (*it);
   *(it++) = and % 10;
   if(and /= 10)
    if(it == vec.end())
    {
     vec.push_back(0);         //知道这个操作会改变容器
     continue;
    }
  }
 }
}
//----------------------------------------------------------------
void Factor::print()
{
  size_t bit = 0;
 for(iter it = vec.end();it != vec.begin();--it)
 {
  if(bit % 50 == 0)
   cout <<endl<<"第"<<setw(3)<<(bit/50+1)<<"个50位:";
  cout << size_t( *(it-1) );
  ++bit;
 }
 cout<<endl;
}

 

这个程序利用了vector容器的动态添加,不需要一次添加那么多,不过感觉执行效率也许并比不上一次添加那么多,不过程序的大部分理解起来要更简单了,比如不需要提前用对数算好需要多少空间,这可以减轻很多负担,特别是根本不知道怎么算的时候,但是这样做付出了代价,就是主运算函数明显更加复杂,因为你还要在循环里面动态处理vector,我就是在这个里面出了问题,请高人给我一些提示。
 

阅读全文....