sinablog2wordpress:从新浪博客搬家到WordPress

Filed Under (新长征路上的代码) by 逆铭 on 2010-02-06 21:23

Screenshot

要从新浪搬到Wordpres,网上广为流传的方法是利用blogbus的博客搬家服务获得blogbus格式的xml,然后再用一个Python写的脚本把它转换成WordPress认识的格式。但是这种方法在最近新浪博客升级以后就失效了。于是自己用现学的写了一个小程序,搬家时能保留标签目录评论评论回复这些信息。

猛击[这里]下载。注意,此程序仅支持2010年初的新版新浪博客,之前或之后的版本都不支持。

要运行程序,你需要确保已经安装过JRE。双击运行后显示如图界面,填入自己的博客地址(不要省略“http://”),然后点击“Start”即可。这时“Start”按钮变为灰色,标题栏显示“Extracting”。等待几分钟,当标题栏显示为“Done”、“Start”按钮重新变为可用时,程序所在目录下会出现一个blog.xml文件。把这个文件直接导入WordPress就可以了。

代码也打在jar包里了,MIT协议。欢迎报告bug。

下面是废话。

恩由于新浪用了ajax,评论信息是通过xhr异步读取的,用一般的方法没法抓到。我纠结许久,最后是用了非常ad hoc的方法解决的,不知道有没有什么什么不太麻烦的通用解决方案呢。

再扯两句。我都想不起来当初具体是怎么想到要学的,也许是为了了解下函数式编程,也许只是想在jvm上有一个喜欢的语言吧——Java写起来太不爽了;Java社区的低效和保守也已经开始显出C++的影子。

确实是非常强大和灵活;我在见到一些颇富技巧性的hack之后都有些怀疑社区的风气会不会慢慢变得像C++社区一样过分热衷技巧的炫耀。不过的设计目标就是以较简单的语法规则获得最大的scalability,不需要通过挖掘语言规范里的犄角旮旯来实现一些必要功能,所以不会像C++一样成为一门本身已相当复杂,却还需要别人反过来教语言发明者如何使用的语言。

毕竟表现力比Java强太多,代码也简洁太多。比如这次我需要实现一个抛出异常后重试若干次的逻辑,只需定义一个函数:

def tryFor[T](times: Int)(op: => T): T = {
  if (times <= 0) throw new RuntimeException("Operation failed.")
  try { return op } catch {
    case e: Throwable => e.printStackTrace
  }
  tryFor(times - 1)(op)
}

然后这样使用:

val source = tryFor(5) {new Source(url)}

程序就会不断获得网页源代码,并在5次失败后抛出异常。Java实现同样的东西可不会如此优雅了。又如下面这段代码返回一篇博文xml:

private def generateEntryXml(entry: BlogEntry) = {
  <item>
    <title>
      {entry.title}
    </title>
    <wp:post_date>
      {dateFormat.format(entry.postDate)}
    </wp:post_date>
    <category>
      {entry.category}
    </category>
    {for (tag <- entry.tags) yield <category domain="tag">{tag}</category>}
    <content:encoded>
      {xml.Unparsed(handleNewLines(entry.content))}
    </content:encoded>
    <wp:status>publish</wp:status>
    {for (comment <- entry.comments) yield generateCommentXml(comment)}
  </item>
}

注意,xml标签直接作为的源代码的一部分在代码中出现!虽然我觉得这样会使语言多出一种“特殊情况”,增加语言的复杂性,但不得不承认这样的设计确实非常优美简洁。

我比较看好,以后自己做跑在jvm上的东西应该是首选语言。推荐有兴趣的童鞋也了解一下。


Tags:, , , ,

小试模板元编程

Filed Under (新长征路上的代码) by 逆铭 on 2009-03-01 14:37

很久不更新了,再水一下吧。

前两天新开的数据结构课布置上机作业,里面又出现了不知道之前出现过多少次的输出质数。每次都交表也会很乏味,所以……这次还是要交表 – -||| 不过要玩一点小花样——让编译器在编译期把质数表算出来。

这个当然涉及到一点模板元编程了。之前虽然看了《Effective C++》里关于模板元的简介挺感兴趣,但看到《学习C++:实践者的方法》里告诫不要在这种“20%场景下的复杂性”上白花时间(“这些细节或技术在日常编程中极少用到,尤其是各种语言缺陷衍生出来的workarounds,构成了一个巨大的长尾……绝大多数只在库开发当中需要用到”),我一直对模板元编程敬而远之。这次也只是消遣一下,并无深入学习的打算。

以下是代码:

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>

std::vector<int> Primes;

template <int toTest, int factor> // factor should be odd
class IsPrime
{
   public:
      enum {
         result = ( toTest == 2 )
         ||  toTest % factor
          && IsPrime < toTest , factor - 2 >::result
      };
};

template<int toTest>
class IsPrime<toTest, 1>
{
   public:
      enum {result = ( toTest == 2 )  || ( toTest & 1 ) };
};

template <int upperBound> // upperBound should be odd or 2
class PrimePick : public PrimePick < upperBound - 2 >
{
   public:
      enum {
         isPrime = IsPrime < upperBound, ( upperBound >> 1 ) | 1 >::result
      };
      PrimePick<upperBound>() {
         if ( isPrime )
            Primes.push_back ( upperBound );
      }
};

template<>
class PrimePick<2>
{
   public:
      PrimePick<2>() {
         Primes.push_back ( 2 );
      }
};

template<>
class PrimePick<1> : public PrimePick<2> {};

int main()
{
   PrimePick<999> PrimeInitializer;

   int m;
   std::cin >> m;
   for ( int i = 0; i < m; ++i ) {
      int n;
      std::cin >> n;

      std::vector<int>::iterator end = Primes.begin();
      while ( end != Primes.end() && *end <= n )
         ++end;

      std::ostream_iterator<int> out ( std::cout, " " );
      std::copy ( Primes.begin(), end, out );
      std::cout << std::endl;
   }
}

原理比较简单,主函数第一行初始化 PrimeInitializer 时,由于继承关系,构造函数会层层递归调用,从里至外利用另一个模板类 IsPrime 判断模板参数是否是质数,并把测试确认的质数放进一个 vector 里,这样就得到了编译期计算出的质数表。IsPrime 则使用最原始的试除方法判断质数。代码里一些写得很纠结的地方,一方面是为了尽量简化计算,减少编译时间,另一方面更主要是因为 g++ 默认最大只能实例化500层模板,以题目的数据规模(1000)不纠结一下编译器会抱怨。

这个程序……很不幸没能 AC。Buaa 的 OJ 在设计时估计就考虑了这种情况,对编译时间做出了限制,在编译20秒左右还没编译成功时会结束编译,直接判 CE……这个程序在我的系统上编译需要20分钟(VC 编译)到半个小时以上( g++ 编译)的时间,冬冬的64位 Ubuntu 上用 g++ 编译也需要将近10分钟时间。如果OJ不做这个限制估计会像当年vijos一样pending很多页吧……

恩,第一次模板元编程经历就以这样悲惨收场了 T_T

更新:

vijos果然被卡住了 – -

http://gallery.tomtung.com/pictures/vijos-puppy-stuck.jpg

可怜的puppy

已作为bug报告。

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>

std::vector<int> Primes;

template <int toTest, int factor> // factor should be odd
class IsPrime
{
public:
enum {
result = ( toTest == 2 )
||  toTest % factor
&& IsPrime < toTest , factor – 2 >::result
};
};

template<int toTest>
class IsPrime<toTest, 1>
{
public:
enum {result = ( toTest == 2 )  || ( toTest & 1 ) };
};

template <int upperBound> // upperBound should be odd or 2
class PrimePick : public PrimePick < upperBound – 2 >
{
public:
enum {
isPrime = IsPrime < upperBound, ( upperBound >> 1 ) | 1 >::result
};
PrimePick<upperBound>() {
if ( isPrime )
Primes.push_back ( upperBound );
}
};

template<>
class PrimePick<2>
{
public:
PrimePick<2>() {
Primes.push_back ( 2 );
}
};

template<>
class PrimePick<1> : public PrimePick<2> {};

int main()
{
PrimePick<999> PrimeInitializer;

int m;
std::cin >> m;
for ( int i = 0; i < m; ++i ) {
int n;
std::cin >> n;

std::vector<int>::iterator end = Primes.begin();
while ( end != Primes.end() && *end <= n )
++end;

std::ostream_iterator<int> out ( std::cout, ” “ );
std::copy ( Primes.begin(), end, out );
std::cout << std::endl;
}
}


Tags:,

初来北航一个多月的零零碎碎

Filed Under (生活在此处) by 逆铭 on 2008-10-29 00:19

http://gallery.tomtung.com/pictures/reminder_look_at_reminder_wall.jpg

几乎持续忙碌中。得空扯两句。

除了多了不少限制,生活和来之前差不多。食堂挺好。大运村很贵,不过想想也就4年而已,也就无所谓了。没阳台,很不方便。房间里有小强出没让人很不爽,不过比去年在福州一中见到的要小多了,也没那么嚣张。还好能有自己的书桌,用无线路由上着网,看着书,其实还是很惬意的。恩……我其实还是很容易满足的。

室友都很好很强大,让我觉得笨鸟先飞格外必要。有点后悔假期没有看看数学,现在一周五节数学(三节高数两节离散)让人有点吃不消。尤其是高数,大半年没有怎么做过这种题,现在看着它们感觉脑子像卡住了一样……英语依旧荒废中,腾不出手来处理,所幸 RP 足够好进了 B 班。专业方面,终于还是失去了第二遍读完 C++ Primer 的耐心,这份读书笔记也不知何年何月才能完成了。转而开始看 Effective C++,看完那三本 Effective 就不再在 C++ 上过多纠缠了,什么模板元之类的都省省吧……这门语言的复杂程度已经足够让我望而却步了,抱着厚书啃啊啃也啃不完总不是办法,还是今后在实践中逐渐积累吧。《Pro C# with .NET 3.0 Special Edition》刚到第八章,《Head First Design Patterns》也到第八章。它们的阅读进度都由于要顾及数学被严重拖慢了,很让人郁闷。至于其它七七八八的科目,什么语文啊计算机导论啊历史啊政治啊航空航天概论啊,都在摸着石头瞎混中,但愿能混过去……

没参加任何类似学生会或者班委会的组织。社团仅仅参加了 MSTC(微软技术俱乐部),并且顺利混进 .NET 组。据冬冬说,我在面试时极为成功的装X竟然让某巨牛印象深刻并颇为赞许,这让我相当有成就感。如果顺利,不太久会参与项目开发。看来我放假时决定学 C# 还算明智。现在感觉 MSTC 还算不错。

本来准备参加的 GC(Google Camp)也顺利入围技术部,不过了解更多以后觉得很不靠谱,暂且退出免得浪费时间。

ACM/ICPC 终于还是没有去参加……恩,连新生的选拔赛都干脆没去。虽然这个决定是早在年初就做好的,心中还是免不了一丝不舍。希望我没有做出错误的选择。

之前参加了一次 Liunx of BUAA 北航小聚,用 Vista 的我只能拿本里的 andLinux 充数。群牛闪耀,气氛很融洽,更重要的是我和冬冬很幸运地遇到 Jesse,他是在北航举行的08年 Gnome 亚洲峰会主页)北航方面的负责人,然后我们就高高兴兴地成了志愿者。记得是蒋天正还是谁给我们说,你们刚来没几天就能参加国际会议了~~ 作为技术上太水的人,我也就只能搬搬桌子、指个路什么的,以此为开源社区做点贡献了。顺带一提,这样一个技术会议的组织者竟然是 Emily 姐和 Pockey 姐两位女性,真是奇妙。会议很成功,这里有n多大家拍的照片。以此为契机,北航的开源社团似乎也即将成立,真是件好事。

一段时间听了很多碟。周董的新专辑还不如上一张,周导演果然没时间玩音乐了;JJ的新专辑安排很杂,不过整体质量还算不错。重新开始听原来没什么感觉的门德尔松无词歌。又开始迷巴赫,重听了以前没有特别留心感受的曲子,从大无开始,到小无,然后是钢琴的帕提塔、意大利协奏曲等等,并且各同时找好几个版本对照。上课路上看着阳光透过教学区树木的树冠,随着欢欣的库兰特舞跳动,或者眯起眼睛依着一首萨拉班德打一会儿瞌睡,觉得生活真是美好。托卡塔从耳旁流过时似乎可以提神,严谨紧凑的赋格似乎也会潜移默化地帮助思考。温暖且踏实的大调诠释着幸福,清冷或宁静的小调表达着自省,二者交替间很容易就在我的心室中共鸣回响起来。从不滥情的巴赫总能让人在忙碌中保持安稳,我每过一段时间似乎都会到巴赫这里停靠。每次少则一周多则一两个月,总能得到珍贵的体验。

恩……似乎这段时间值得说的就是这么多。瞎忙之中一切看起来都在向着好的方向发展,一些小麻烦还不足以影响心情。希望一切顺利。

最后,祝贺银男 Ghost 终于银了。祝高三各位大牛保送顺利。欢迎报考 BUAA。


Tags:, , , , , ,

写在临行前

Filed Under (生活在此处) by 逆铭 on 2008-09-18 17:27

昨天终于顺利闹完了,20号开学,今晚的火车,明天到。

如果从年初学校定下来开始算起,到现在,那真是有大半年的假期了。虽然基本上一直都是忙忙碌碌的,但是效率并不咋高。假期开始的时候没写什么假期计划,现在就写点总结吧。

E文的重要性我是清楚的,自己清楚确实需要加强。原来有人(好吧,我坦白,包括我自己)觉得我英语不错,其实现在客观地看,不过是几句简单的说得比较溜,能吓唬下不了解情况的人而已,其实在基础各方面问题非常多。

我差不多是从今年4月份开始重新拾起E文的。兰州找不到合适的班,就自己跟着一个叫《彭蒙惠英语》的杂志走。这个是空中英语教室系列的高级版,对我来说比较吃力,但是中级版又太EZ,只好勉强用这本,结果每个月甚至都没有精力看完当月的内容。4~8月读过的课文篇数分别为2、6、7、8、1,可见利用率并不高。

当初其实规划得挺好:每天都安排课文和配套广播,可以练阅读和听力;遇到的生词记下来可以提升词汇量;另外还在网上找到了语音讨论组可以练习口语。这样就可以全面提高了。但是对我来说杂志内容难度实在偏大,每天都要花费不少时间。每天如果读课文至少需要半小时,预习半小时,广播节目半小时。口语练习一小时。生词仅一共背了 2000左右(更不幸的是现在已经忘掉不少了),难度等级从小学初中到专八GRE都有(可见我野路子学上来的基础确实有问题),每天按照软件安排复习和初记至少半小时。这样算下来单单是英文每天就要最少花费3小时,哪天稍微耽搁后面进度就跟不上了,煞是郁闷,到最后就干脆坚持不下来了。现在还是没有真正找到合适的英语学习方法,比较愁,哪位同学有什么建议欢迎在下面评论。

数学看得不多,除去已经忘掉的就可以忽略不计了……

专业课方面,主要是看完了一本C#和对象建模的入门书,以及C++ Primer。前者非常基础,需要深入起码还要看好几本。后者,正如我在这里所说的,花了超出预期很多的时间才看完第一遍,第二遍也没能在开学前看完。本来原计划是在开学前不但看完Primer,还要看完那三本Effective的,彻底搞定明年新标准发布前我需要掌握的C++内容。已经完成的部分和计划相比差太多了。

学会了游泳。

自学一点钢琴,教材用599和哈农,从高考开始学到现在,各只弹到第15首。买的琴是Yamaha DGX-620,本来想带去学校继续学的,结果发现我根本没有理解20公斤意味着什么……况且宿舍小也不一定能放得下。原来所谓Portable Grand的意思仅仅是相对Grand稍微Portable 一点而已了。查了下其它的电钢,似乎真正比较Portable的键盘都不带配重……那就先算了吧。

恩……大致就是以上这些了。Linda姐问我假期最大的成果是什么,我说不知道……各项都不满意。忙忙碌碌但不出效果。其实把C++和E文放在一起的安排是很失败的,一天到晚都在看语言上拉拉杂杂的东西,搞得人又烦又低效。。。

忙忙碌碌的,看起来那么长的一个假期也就这样过去了。昨天去一中,见了老赵、DF、野牛、格格他们,又在楼道里习惯性闲逛,在二球下习惯性驻足,在公交站台上习惯性无意义等待。即将满载着回忆离开,除了少了很多熟识的面孔,那里的一切都还是一样熟悉。

之前同学陆陆续续都走差不多了,并没有太多离别的伤感。我只是觉得大家保持着联系就好,有缘也肯定还会再见。我似乎总是习惯看好现在,看好将来,并不太多留恋身后。想想旅行时我不喜欢拍很多照片留念可能也是这个原因吧。是豁达,是冷漠,还是不知珍惜,我自己无从分辨。我一直自以为是前者,只是昨天从姥姥家回来才产生了怀疑。

姥姥快八十岁了,坚持要在我走之前看看我。她对我絮絮叨叨了很多已经说过很多遍的事情,然后让我常给她打电话。我们都笑着说姥姥拿起电话就豆腐三碗、三碗豆腐说个没完没了了,长途话费下来肯定把人愁死。姥姥摇着头说,她知道长途花费贵,又说我学习紧张,又说宿舍里那么多人也影响别人,只要我每周周末晚上打个电话就行了,她就和我说一句话,就一句,说好着呢,听听我的声音马上就挂掉……一边说用手掌抹着已经发红的眼眶。我们赶紧解释说长途花费是按时间计的,没有那么贵,学习也并没有那么紧张,也不会打扰到别人。姥姥还是摇着头说没事的时候多休息不要打电话,每周打一次电话,说一句话听听我的声音就行了,就一句话就行了……絮叨地这么说了很多遍。

我有些不知所措,突然间感到亲人对自己如此强烈的不舍。看着姥姥发红的双眼,我突然觉得不忍离去。这是一种我其实一直没有真正明白的情感,曾经很多老师对此的解释都是“等你们做父母、祖父母了就明白了”。作为一个自私的独生子女,我想我并没有真正懂得什么是爱。

我只是愣在那里,不断提醒自己记得多打几个电话。除此之外我不知道还能做到什么。

Unable To Stay, Unwilling To Leave. 不管怎样,几天以后就是完全不同的生活了。



Tags:, , , ,

C++ Primer 读书笔记 – 第八章

Filed Under (新长征路上的代码) by 逆铭 on 2008-09-02 15:12

[笔记索引]

第8章 标准IO库

⒈ IO标准库类型
类名           派生自            头文件       描述
istream        ios               iostream   输入流
ostream        ios               iostream   输出流
iostream       istream和ostream  iostream   输入/输出流
ifstream       istream           fstream    输入文件流
ofstream       ostream           fstream    输出文件流
fstream        iostream          fstream    输入/输出文件流
istringstream  istream           sstream    输入字符串流
ostringstream  ostream           sstream    输出字符串流
stringstream   iostream          sstream    输入/输出字符串流

⑴ 标准库分别实现了以上继承层次的的两个版本,分别面向 char 类型(如上)和 wchar_t 类型
后者采用和前者一样的命名规则,仅在每个类和对象名前加上字母w以示区别
⑵ IO对象不可赋值或赋值

⒉ 标准输入输出对象
extern istream cin; 标准输入流
extern ostream cout; 标准输出流
extern ostream cerr;  标准错误输出流
extern ostream clog; 标准日志输出流

⒊ 文件输入输出流
⑴ 使用 ifstream, ofstream 和 fstream 型对象可以实现对文件的读写
⑵ 可以调用公共成员函数 open 或在创建时使用构造函数使流对象关联到一个文件
二者都接受两个参数:一个C风格字符串表示文件名,一个可选的 ios_base::openmode 型值表示流打开模式
① openmode 是用以表示流打开模式标识的位掩码类型,支持位操作符
该类型对象的值可以是以下打开模式标识或它们之间进行位操作所得的结果
和以下(openmode 型)模式标志均为 ios_base 的公共成员
Ⅰ app (append)每次执行输出操作前都定位到流的末尾
Ⅱ ate (at end)打开时定位到流末尾
Ⅲ binary 在二进制(而非文本)模式下操作
Ⅳ in (input)允许输入操作
Ⅴ out (output)允许输出操作
Ⅵ trunc (truncate)抛弃流中现存的所有内容,打开时假定长度为0
(openmode 和上述标识分别是 ios_base 的公共成员类型和公共成员常量)
② fstream 默认使用 in | out 模式
ifstream 自动使用 in 模式
ofstream 自动使用 out 模式(效果上等同于 out | trunc)
⑶ 完成文件操作后可以使用公共成员函数 void close() 关闭
关闭后可以再次 open 新文件,但之前应调用 clear 清除该流的状态
流对象被析构时也会自动调用 close()

⒋ 字符串流
⑴ 常用于特定数据类型和格式化的字符串之间的相互转换
3个类的构造函数各有带或不带 string 形参的两个不同版本
使用带 string 参数的版本,则创建储存该实参副本的字符串流对象
3个类的都有公共成员函数 str,它的两个版本分别返回和设置与该流对象相关的 string 对象
string str() const;
返回与该流对象相关的 string 对象
string str(const string&);
将该流对象相关的 string 对象设置为实参的副本

⒌ 条件状态
⑴ 流对象内都用一个 iostate 型值表示条件状态
· iostate 是用以表示流错误状态标志的位掩码类型,支持位操作符
该类型对象的值可以是以下条件状态标识或它们之间进行位操作所得的结果
Ⅰ eofbit 输入操作中遇到文件结束符(同时会设置failbit)
Ⅱ failbit 输入操作失败(通常可恢复)
Ⅲ badbit 流被损坏(通常不可恢复)
Ⅳ goodbit 没有错误,值为0
(iostate 和上述标识分别是 ios_base 的公共成员类型和公共成员常量)
⑵ 查询条件状态
bool ios::eof() const;
eofbit位被设置则返回 true, 否则返回 false
bool ios::fail() const;
failbit 或 badbit 位被设置则返回 true, 否则返回 false
bool ios::bad() const;
badbit 位被设置则返回 true, 否则返回 false
bool ios::good() const;
流对象有效则返回 true, 否则返回 false
ios_base::iostate rdstate() const;
返回当前条件状态
⑶ 控制条件状态
void ios::clear( ios_base::iostate = goodbit);
使用参数值替换当前条件状态
void ios::setstate( ios_base::iostate );
将参数状态加入当前状态(如同使用了位或操作符|)
⑷ 可以直接检查流对象的真值来确认是否可用

⒍ 输出缓冲区的管理
每个IO对象都管理一个缓冲区,作为流和写入目标间的媒介
下面几种情况将导致输出流被刷新(即写入到真实的输出设备或者文件)
⑴ 程序正常结束或目标文件被关闭
注: 程序崩溃不会刷新缓冲区
调试崩溃的程序时如果用最后的输出定义错误位置,应确保缓冲区已刷新
因此输出时应多使用endl而非‘\n’
⑵ 当缓冲区满时会自动刷新
⑶ 使用操纵符显式刷新缓冲区
(以下均定义在<ostream>中)
① endl 输出换行符并刷新缓冲区
② flush 刷新缓冲区(不添加任何字符)
③ ends 输出空字符‘\0′并刷新缓冲区
注:cplusplus.com,cppreference.com和msdn上都没有表示ends会刷新缓冲区,存疑
⑷ 使用 unitbuf 操纵符可以设置流对象的相关格式状态标志,使缓冲区在每次插入操作后都刷新
使用 nounitbuf 操纵符可以恢复为正常的、系统控制的缓冲区刷新方式
⑸ 可以把流对象绑定到一个输出流对象,前者进行任何I/O操作都会刷新后者
① 可以使用 ios::tie 公共成员函数查询、修改绑定状态
ostream* ios::tie() const;
返回指向所绑定到的输出流对象的指针
ostream* ios::tie( ostream* );
绑定到参数中指针指向的输出流对象(实参为0则解除绑定),并返回指向原绑定对象的指针
② cin, cerr, clog 默认绑定到 cout, 而它们的宽字符版本则默认绑定到 wcout

⒎ 控制格式状态
istream/ostream(及它们的派生类)对象都支持使用提取操作符(>>)/插入操作符(<<),从流中提取/向流中插入格式化的对象数据
把提取/插入操作符和一些操纵符(manipulator)一起使用,可以改变流对象的特性或格式设置
其中除setbase,setprecision,setw,setfill定义在头文件<iomanip>中外,其余操纵符均定义在<ios>中
⑴ 布尔值格式
从流中提取/向流中插入布尔值时形式使用 0, 1(默认) 还是 false, true
· 使用操纵符 boolalpha noboolalpha
⑵ 整数格式
① 基数设置
从流中提取/向流中插入整数时使用八进制、十进制(默认)还是十六进制
· 使用操纵符 oct dec hex
· 使用操纵符 setbase(int) (实参只能是8,1016)
② 基数前缀设置
向流中插入整数时是否显示基数前缀(八进制的0和十六进制的0x)(默认不显示)
· 使用操纵符 showbase noshowbase
③ 十六进制和科学计数法中字母的大小写设置
向流中插入整数时若使用十六进制或科学计数法,出现的字母使用小写(默认)还是大写
· 使用操纵符 uppercase nouppercase
⑶ 浮点数格式
① 精度设置
向流中插入浮点数精度为多少(默认为6)
· 使用 ios_base 的公共成员函数 precision 可以获得和设置流的浮点数精度
streamsize precision() const;
返回流对象的浮点数精度
streamsize precision( streamsize );
设置流的浮点数精度为实参值并返回先前的精度值
· 使用操纵符 setprecision( streamsize )
以上 streamsize 为表示流中大小的实现类型,是一个带符号基本整型的别名
注: 精度的解释在使用不同情况下不同
· 默认情况下解释为最大有效数字(位数不足不用小数末尾的零补齐)
· 在强制使用fixed,scientific或showpoint时解释为小数点后的确切位数(位数不足则用小数末尾的零补齐)
② 计数法设置
向流中插入浮点数时,让系统自动选择计数法(默认),还是强制指定为定点计数法或科学计数法
· 使用操纵符 fixed scientific
· 计数法不存在恢复默认的操纵符,但可以通过给成员函数 unsetf 传入常量 floatfield 达到目的
(unsetf 和 floatfield 均为 ios_base 的公共成员)
③ 小数点显示设置
向流中插入浮点数时,若小数部分为0,是否显示小数点和小数部分的0(默认不显示)
· 使用操纵符 showpoint noshowpoint
④ 非负数(整数和浮点数)正号设置
向流中插入非负数(包括0)时是否显示正号+ (默认不显示)
· 使用操纵符 showpos noshowpos
⑷ 对齐和填充格式
① 使用操纵符 setw( streamsize ) 可指定下次对流的插入操作的最小字段长度
注: 和endl一样,它不改变流的内部状态,只影响下一个输出
② 使用操纵符 setfill( char ) 设置向流中插入对象时用来填充字段的字符(默认为空格)
③ 字段内对齐方式设置(默认左对齐)
· 使用操纵符left right internal
关于internal:
- 对数值表示左对齐前缀(正负号和十六进制的0x)右对齐数值
- 对非数值则与 right 等效
⑸ 空白字符的处理
· 使用操纵符 skipws noskipws 设置从流中提取对象时是否忽略空白字符(默认忽略)
· 使用操纵符 ws 忽略输入序列中从当前位置开始尽可能多的空白字符,直到遇到非空白字符才停止
当然,如果已经(默认)设置了 skipws 则没有必要使用 ws

⒏ 无格式I/O操作
⑴ 单字节操作
① istream& get ( char& );
istream 的公共成员函数,从流中提取一个字符存入实参中
int get( );
无参数版本从流中提取一个字符并返回其值
② ostream& put ( char );
ostream 的公共成员函数,将字符参数值写入输出缓冲区并返回*this
③ istream& putback ( char );
istream 的公共成员函数,将字符参数值放回流中并返回*this
④ istream& unget ( );
istream 的公共成员函数,将流退回1字节并返回*this
int peek ( );
istream 的公共成员函数,读取并返回流中的下一个字符,但不提取它
注: 为允许返回EOF,put(无参数版本)和peek的返回值都为 int
⑵ 多字节操作
① istream& get (char* s, streamsize n, char delim );
istream 的公共成员函数,不断从流中提取字符并以字符串形式存入s指向首地址的数组,
直到出现以下情况之一:
- 已经提取了(n-1)个字符;
- 遇到了定界字符delim(如果不提供此参数则为‘\n’);
(被找到的定界字符不会被提取,而是继续留在流中作为下一个要被提取的字符)
- 到达文件末尾;
- 提取过程中发生错误;
最后返回*this.
注: 提取的字符以字符串的形式储存,故将自动添加结尾的‘\0′,而被提取的最大字符数也为(n-1)而非n
② istream& getline (char* s, streamsize n, char delim );
istream 的公共成员函数,与上面参数表相同的get版本类似,唯一区别在于定界字符会被提取并丢弃
③ istream& read ( char* s, streamsize n );
istream 的公共成员函数,从流中提取n个字符存入s指向首地址的数组,
直到出现以下情况之一:
- 已经提取了n个字符
- 到达文件末尾(eofbit和failbit会被设置)
- 提取过程中发生错误
最后返回*this.
注: 与get和getline不同,read不把提取的字符保存为字符串,故不会自动加上‘\0′
④ streamsize  gcount ( ) const;
istream 的公共成员函数,返回上次无格式输入操作提取的字符个数
(peek, putback, unget不提取字符,故对此gcount返回0)
⑤ ostream& write ( const char* s , streamsize n );
ostream 的公共成员函数,把s指向首地址的数组的前n个字符写入输出流缓冲区
⑥ istream&  ignore ( streamsize n = 1, int delim = EOF );
istream 的公共成员函数,从流中提取并抛弃字符,直到:
- 已经提取了n个字符或遇到了定界字符delim(该字符不会被提取)
最后返回*this.

⒐ 流的随机访问
⑴ 用于随机访问的成员函数
· pos_type tellg ();
pos_type tellp ();
返回输入/输出流中的标记当前的绝对位置
· istream& seekg ( pos_type pos );
ostream& seekp ( pos_type pos );
将输入/输出流中的标记重新定位至参数值表示的位置
· istream& seekg ( off_type off, ios_base::seekdir dir );
ostream& seekp ( off_type off, ios_base::seekdir dir );
将输入/输出流中的标记重新定位至距离dir偏移量为off的位置
⑵ 关于上述函数参数和返回值的类型
① pos_type和off_type都是类的成员类型,分别表示流中的标记位置和偏移量
后者可正可负,分别表示向前和向后偏移
② seekdir类型用来表示偏移的启示位置,其可能值如下
beg 流的开头
cur 流的当前位置
end 流的末尾
⑶ 以上tell和seek版本分别有两个版本g(get)和p(put)
其区别在于g版是istream类的成员,而p版是ostream类的成员
对于iostream对象,而者都适用,它们都操作同一个标记(而非输入输出各一个)
⑷ 普通iostream对象一般不允许随机访问,以上内容主要适用fstream和sstream


Tags:,

C++ Primer 读书笔记 – 第七章

Filed Under (新长征路上的代码) by 逆铭 on 2008-08-28 17:14

[笔记索引]

第7章 函数

㈠ 函数的声明和定义
⒈ 与变量类似:
⑴ 函数必须在调用前声明
⑵ 函数声明可与定义分离
⑶ 一个函数只能定义一次但可声明多次
⒉ 函数声明由函数返回类型、函数返回类型和形参列表组成
三者描述了函数的接口,称为函数原型(function prototype)
⑴ 函数的操作数,即形参(parameter),在一对圆括号中声明,并以逗号分隔
形参名是可选的,但形参需要在定义函数时命名才能使用
⑵ 函数执行的运算在一个称为函数体(function body)的块语句中定义
⒊ 函数一般在头文件中声明,在源文件中定义
此时应使后者包含前者,以便编译器检查定义和声明是否一致
⒋ 将一个较小的、常被调用的函数指定为
inline 可使函数在调用点展开,以避免调用函数的额外开销
但内联说明对编译器来说只是一个建议,编译器也可能选择忽略

㈡ 函数的调用和参数传递
⒈ 函数的调用
⑴ 使用调用操作符()实现函数调用
① 操作数是函数名和一组(可能为空的)用逗号分开的实参(argument)
② 结果类型为函数返回值的类型,结果为函数的返回值
⑵ 函数调用做两件事:
① 首先(隐式)定义形参,并用对应的实参进行初始化
· 形参的初始化与变量一样:
如果形参为非引用类型则复制实参的值,如果形参为引用类型则它只是实参的别名
② 主调函数(calling function)的执行被挂起,被调函数(called function)开始执行
⒉ 非引用形参
⑴ 指针形参
① 可以通过传入指针来间接访问指针所指对象
② 若需要保护指针指向的对象,可指定形参为指向 const 对象的指针
const 形参
① 用来初始化 const 形参的实参无论是不是 const 都可以
在函数中不能改变该实参的局部副本
② 非引用形参是否为 const 不影响编译器对其所属函数类型的判别
·注:对于指针,注意区分 const 是修饰指针本身的性质还是所指对象的性质
⑶ 不适合复制实参的情况
① 需要修改实参值时
② 需要将大型对象作为实参传递时
③ 无法复制对象时
以上情况可通过将形参定义为指针或引用类型解决
⒊ 引用形参
⑴ 可以使用引用形参返回额外信息
⑵ 可以利用 const 引用形参避免复制实参
⑶ 使用引用形参时,将不需要修改的定义为 const 会更灵活
const 引用形参不能用 const 左值或右值初始化,而定义为 const 则无此问题
⒋ 一般通过传递迭代器来传递 vector 等容器
⒌ 数组形参
⑴ 当把数组直接作为实参传给函数时:
① 若形参不是数组的引用,则自动转换为指向首元素的指针
例如以下三种定义完全等价:
void f(int*); //推荐,明确指出操作对象为指针
void f(int[]);
void f(int[10]);//维数被编译器忽略,写法容易引起误解
② 若形参是数组的引用则不发生转换,函数得到的是数组本身
此时编译器会检查形参和实参数组的维数是否匹配
⑵ 把数组传递给函数处理时,可以:
⒈ 使用标准库规范,即传入首元素指针和超出末端指针
⒉ 传递数组首元素地址,并显式传递表示数组大小的形参
⑶ 可以通过传递指向数组的指针来传递多维数组
⒍ 含有可变形参的函数
为兼容C语言而保留的特性,只能传入简单数据类型,大多数类类型对象都不能正确复制
⒎ 默认实参
⑴ 通过给形参表中的形参提供明确的初值可指定默认实参,如
void f( int, int = 1, int = 2 );
① 默认实参可以是任何适当类型的表达式
② 如果一个形参有默认实参,则其后的所有形参都必须有默认实参
③ 在一个文件中,只能为一个形参指定默认实参一次
通常应在函数声明中指定默认实参,并将该声明放在合适的头文件中
⑵ 调用函数时可省略有默认值的实参:若省略则使用默认实参,否则使用用户提供的实参
· 默认实参只能用来替换函数调用缺少的靠后的实参
因此设计带有默认实参的函数时,应将最少使用默认实参的形参排在最前,最多使用默认实参的形参排在最后

㈢ 局部对象的作用域和生命期
⒈ 函数体是一个作用域,在其中定义的变量称为局部变量(local variable),只可在该函数中访问
局部变量和形参均不能重名
⒉ 自动对象(automatic objects)是局部于函数的对象,会在每次函数调用时重新创建,并在函数结束时撤销
非静态局部变量和形参都是自动对象
static 局部对象确保不迟于在程序执行流程第一次经过该对象的定义语句时初始化,一旦创建在程序结束前都不会撤销
在所在函数被多次调用的过程中,static 局部对象会持续存在并保持它的值

㈣ 函数的返回值和 return 语句
⒈ 函数返回类型可为内置类型、类类型或复合类型,但不能是另一个函数或内置数组类型
void 类型表示该函数不返回任何值
return 语句
用于结束当前函数,将控制权返回给该函数的主调函数
⑴ 没有返回值的 return 语句
① 只能用于返回类型为 void 的函数
② 非必需,隐式 return 发生在函数最后一个语句完成时
⑵ 具有返回值的 return 语句
① 返回类型不是 void 的函数必须返回一个值(主函数 main 除外)
注:应在函数中的每条执行路径末尾都提供 return 语句;若不提供编译器可能也无法发现
② 用函数返回值初始化在调用函数处创建的临时对象,与用实参初始化形参的方法一样
即若返回非引用类型则得到副本,若返回引用类型则得到对象本身
③ 切勿返回局部对象的引用或指针

㈤ 函数的重载
⒈ 出现在相同作用域中的两个函数,如果具有相同的名字而形参表不同,则为重载函数(overloaded function)
仅仅是返回类型不同、默认实参不同、非引用形参的 const 性质不同不能实现重载
⒉ 若局部地声明函数,则该函数屏蔽而非重载在外层作用域中声明的同名函数
⒊ 重载确定的三个步骤
⑴ 确定候选函数集合
候选函数(candidate function)是与被调函数同名的函数,且在调用点上声明可见
⑵ 从候选函数集合中选择可行函数(找不到则此调用错误)
可行函数(viable function)满足两个条件:
① 函数形参与该调用的实参个数匹配(考虑默认实参)
② 每个实参的类型须与对应形参匹配(类型相同或可隐式转换)
⑶ 寻找最佳匹配(有多个匹配程度相同的则调用有二义性)
原则是实参类型与形参类型越接近则匹配越佳。具体匹配程度从高到低为:
① 精确匹配:实参与形参类型相同
② 通过类型提升实现的匹配
③ 通过标准转换实现的匹配
④ 通过类类型转换实现的匹配

㈥ 函数指针
⒈ 定义形式
返回类型 (*标识符)(形参表)
⒉ 可使用 typedef 简化定义
typedef 返回类型 (*自定义类型名)(形参表)
⒊ 对函数指针进行初始化和赋值,可使用:
⑴ 同类型的函数
引用函数名但不调用该函数时,函数名被自动解释为指向函数的指针
因此可以直接使用函数名对函数指针初始化或赋值,不需要使用取地址操作符&
·注:指针类型须与函数完全匹配
⑵ 同类型的函数指针
0值常量表达式
⒋ 通过函数指针调用函数
可以不使用解引用操作符,直接使用函数调用操作符()调用所指函数
⒌ 函数指针作形参时,星号*可写可不写
⒍ 函数的指针作函数返回值
函数指针返回类型 (*函数名(函数形参表))(函数指针形参表)

㈦ 主函数 main
⒈ 返回值
⑴ 主函数返回类型为 int, 返回值在大多数系统中是一个状态指示器
返回0表示程序运行成功,其它大部分值表示失败
⑵ 不需要显式使用 return 语句,编译器将隐式插入返回0的语句
⑶ 为使返回值独立于机器,cstdlib 头文件定义了两个预处理变量
EXIT_FAILURE(运行失败)    EXIT_SUCCESS(运行成功)
⒉ 使用主函数形参处理命令行选项
int main(int argc, char* argv[])
· argv为一个C风格字符串数组,储存了从命令行调用程序时输入的字符串(包括程序名和参数)
· argc表示argv中字符串的个数
⒊ 主函数 main 不允许被显式调用、取地址或重载


Tags:,

C++ Primer 读书笔记 – 第六章

Filed Under (新长征路上的代码) by 逆铭 on 2008-08-24 17:01

[笔记索引]

第6章 语句
㈠ 简单语句
⒈ 表达式语句(expression_r statement)
一个表达式加上结尾的分号,执行时导致该表达式被求值
⒉ 空语句(null statement)
只由一个单独的分号组成,当语法上需要一个语句但逻辑上并不需要时使用
⒊ 声明语句
用于声明或定义对象或类
㈡ 复合语句
⒈ 复合语句(compound statement)又被称为块(block),是用一对花括号{}括起的(可能为空的)语句序列
⒉ 通常用于语法规则要求使用单个语句但程序逻辑需要多个语句时
⒊ 块标示了一个作用域,在块中引入的名字只能在其内部访问
㈢ 控制流语句
注:作为语句控制结构的一部分定义的变量,仅在该语句内可见
⒈ 条件分支结构
if 语句
关于 else-if 匹配的二义性问题: else 匹配给最后出现尚未匹配的 if
switch 语句
switch 在计算表达式的值后跳转到匹配的标号处(无匹配则跳转至 default),并从该点开始一直执行下去,
直至 switch 语句结束或遇到 break 语句
switch 求解表达式的结果须为整型,每个 case 标号的值也须为各不相同的整型常量表达式
switch 内部的变量定义
· 可以在 switch 求解的表达式中定义和初始化变量
· 为防止跳过变量定义,只允许在最后一个标号后定义变量
· 也可以引入块语句,在其中定义变量
⒉ 循环
while 语句
注:循环条件中定义的变量在每次循环时都要经历创建和撤销的过程
for 循环语句
注:语句头中的初始化语句、循环条件和表达式三者都可以省略
循环条件省略表示永远为 true
do while 语句
注:不能在循环条件中定义变量
break 语句
用于结束最近的外围 while, do while, forswitch 语句,并在该语句后继续执行
continue 语句
导致最近的外围循环语句(for, while, do while)正在进行的这次迭代提前结束
goto 语句
goto 语句提供了函数内部的无条件跳转,实现从 goto 语句跳转到同一函数内某个带标号的语句
除非有足够理由,应避免使用 goto 语句
⑵ 在任何语句前提供一个标识符和冒号,就得到一个带标号的语句(labeled statement)
标识符: 语句
使用 goto 语句跳转到该语句: goto 标识符;
由于这里的标识符只能用作 goto 的目标,因此可以与其它类型的标识符(如变量名)同名
goto 语句不能跨越变量的定义语句向前跳转
若确实需在 goto 和跳转目标位置间定义变量,则须定义在块中
try, catch 语句和 throw 表达式
用于异常处理
return 语句
用于结束当前函数,返回函数被调用处继续执行

⒍⒕ 使用预处理器进行调试
⒈ 使用 NDEBUG 预处理变量实现有条件的调试代码(类似头文件保护符)
#ifndef NDEBUG
#define NDEBUG
// 调试代码
#endif
如果定义了 NDEBUG 就不执行调试代码
⒉ 使用 NDEBUG 预处理变量以及 assert 预处理宏
定义在头文件cassert中,常用来检查不可能发生的状况,形式为
assert(表达式)
如果表达式结果为 false, assert 输出信息并终止程序
如果定义了 NDEBUG 预处理变量,assert 将被忽略,不会产生任何运行时代价
⒊ 预处理器定义了四种在调试时有用的常量
__FILE__ 文件名
__LINE__ 当前行号
__TIME__ 编译时间
__DATE__ 编译日期


Tags:,

C++ Primer 读书笔记 – 第五章

Filed Under (新长征路上的代码) by 逆铭 on 2008-08-23 01:31

[笔记索引]

第5章 表达式
⒈ 表达式(expression_r)是一个C++程序中最低级的计算,由一或多个用一个操作符(operator)连接起来的操作数(operands)组成
⒉ 每个表达式都产生一个结果。表达式可以用作操作数,因此可用多个操作符编写复合表达式
⒊ 在求解表达式的过程中如果需要储存运算结果,编译器会自动创建没有名字的临时对象(temporary object),这些对象会在外围最大的表达式结束后释放
⒋ 表达式是否合法、合法表达式含义如何(执行什么操作、结果是什么类型)均取决于操作数的类型

⒌⒈ 算术操作符
⒈ 按优先级从高到低排列为:
一元正+, 一元负-; 乘法*, 除法/, 取模%; 加法+, 减法-
⒉ 关于除法/
⑴ 两整数相除结果仍为整数,商的小数部分被截去
⑵ 一正一负两整数相除,结果值向
0一侧还是向-∞一侧取整依赖于机器
⒊ 关于取模
⑴ 操作数只能为整型
⑵ 两操作数均为负时结果为负或0;
两操作数一正一负时,结果的符号随哪个操作数而定依赖于机器

⒌⒉ 关系操作符和逻辑操作符
⒈ 按优先级从高到低排列为:
逻辑非!; 小于<, 小于等于<=, 大于>, 大于等于>=; 相等==, 不等!=; 逻辑与&&; 逻辑或||
⒉ 关系操作符和逻辑操作符接受算术或指针型操作数,并返回 bool 型值
逻辑操作符视其操作数为条件表达式
⒊ 逻辑与&&和逻辑或||操作符支持短路求值(short-circuit evaluation)
⒋ 不应串接使用关系操作符
形如i<j<k的表达式得不到预期结果

⒌⒊ 位操作符
⒈ 按优先级从高到低排列为:
位取反~; 左移<<, 右移>>; 位与&; 位异或^; 位或|
⒉ 位操作符使用整型操作数,将其视为二进制位的集合
由于负整数的符号位如何处理依赖于机器,因此应使用 unsigned 整型操作数

⒌⒋ 赋值操作符
⒈ 赋值操作符的左操作数须为非 const 左值
赋值表达式的结果即为其左操作数(左值)
⒉ 赋值操作符从右向左结合,因此当各操作数都有相同的通用类型时,允许在一个表达式中进行多次赋值,如:
i = j = k = 0;
⒊ 复合赋值操作符
对于任意二元算术操作符或二元位操作符 op
a op= b;
相当于
a = a op b;
二者显著的差别在于前者只计算了一次左操作数,后者则计算了两次

⒌⒌ 自增和自减操作符
⒈ 自增++和自减–操作符为对象加1或减1提供了方便简短的实现方式,有前置和后置两种使用形式
前置操作返回加(减)1后的对象(左值),后置操作返回操作数的原值(右值)
⒉ 出于性能考虑,应只在必要时才使用后置操作符
后置操作符需要先保存原值以便返回,而前置操作符只需加(减)1后直接返回对象即可

⒌⒍ 箭头操作符
箭头操作符用于获取指针指向类类型对象的成员。
下面两个表达式等价:
p->foo;
(*p).foo;

⒌⒎ 条件操作符
⒈ 条件操作符是C++中唯一的三元操作符,能将简短的 if-else 语句嵌入表达式
格式为 cond ? expr1 : expr2
首先计算 cond 值,若为 true 则计算并返回 expr1, 为 false 则计算并返回 expr2
⒉ 避免深度嵌套以保证可读性

⒌⒏ sizeof 操作符
sizeof 操作符用于返回一个对象或类型的大小(单位字节,类型 size_t)
形式为 sizeof expr
或  sizeof(type_name)
sizeof 表达式的结果是编译时常量
对表达式使用 sizeof 时该表达式的值并不会被计算
⒊ 对数组作 sizeof 将得到整个数组在内存中的长度

⒌⒐ 逗号操作符
逗号表达式是一组由逗号分隔的表达式,从左向右计算并返回最右边表达式的值(若该表达式为左值则返回左值)

⒌⒒ newdelete 表达式
newdelete 表达式分别用于动态创建和释放单个对象或一个数组
new 表达式
new 表达式动态创建单个对象或一个数组,并返回指向该对象或数组首元素的指针
① 动态数组维数为0值时也将返回有效的非零指针,但不能解引用
② 可以创建动态的 const 对象或数组,它们无法修改但可以释放
③ 如果无法获取需要的空间,系统将抛出 bad_alloc 异常
⑵ 分配与对象(或数组元素)初始化
① 默认内置类型不初始化,类类型调用默认构造函数(必须提供)
单个对象: new [const] 类型名
一个数组: new [const] 类型名[维数]
② 添加空括号()可执行值初始化(value initialization)
即内置类型对象置为0,类类型对象调用默认构造函数(必须提供)
单个对象: new [const] 类型名()
一个数组: new [const] 类型名[维数]()
注:对于后者,我使用的编译器中,VC初始化后会把元素置为0,但mingw不会
③ 执行直接初始化
单个对象: new [const] 类型名(初始化式)
动态数组元素不支持类似初始化方式
delete 表达式
⑴ 由 new 动态分配的对象或数组需要显式地使用 delete 表达式释放(否则会造成内存泄漏)
delete ptr 和 delete[] ptr 分别释放单个对象和动态数组
① 如果指针不指向由 new 分配的对象,则对其 delete 不合法
例外:对零指针 delete 是合法的
② 读写已释放的对象或对同一内存空间多次 delete 都可能导致错误
因此 delete 之后应立即重置该指针的值(一般重置为0)
③ 释放动态数组如果丢掉方括号[],将可能导致编译器无法发现的错误

⒌⒑ 复合表达式的求值
⒈ 含有两个或以上操作符的表达式称为复合表达式(compound expression_r)
操作数和操作符的结合方式决定了符合表达式的值,而前者取决于操作符的优先级和结合性:
操作数优先与优先级更高的操作符结合;操作符优先级相同时则由结合性决定结合方向
⒉ 圆括号()凌驾于优先级之上
若不确定结合方式,则使用圆括号()强制确定操作数的组合
⒊ 结合方式确定并不意味着操作数的计算顺序并不确定
若修改了操作数的值,则不要在同一语句的其它地方再使用它,除非操作数的计算次序不成问题
运算符优先级与结合性表

⒌⒓ 类型转换
⒈ 隐式类型转换
两个类型间存在可转换关系,则称两个类型相关
⑴ 何时发生
当编译器期望获得某种类型的数据却得到另一种类型的,就会尝试自动转换类型
具体情况包括:
① 表达式中不同类型的多个操作数被转换到同一类型
② 用作条件的表达式被转换为
bool
③ 用一表达式对某变量初始化或赋值时,前者转换为后者的类型
④ 给函数传递实参和返回值时可能发生隐式类型转换
⑵ 内置类型转换规则
① 算术类型间的转换
ⅰ 浮点型转换为整型时小数部分被抛弃(在赋值、初始化等情况下发生)
ⅱ 二元(算术、关系等)操作符表达式中,为试图保留精度会把较小的类型转换为较大的类型
a类型提升
· 将比 int 小的整型提升为 int 或(当 int 不足以容纳原类型的所有可能值时)unsigned int
· 将 float 提升为 double
signedunsigned 之间的转换
· 对于 signed 较小类型和 unsigned 较大或相同类型,前者转换为后者
· 对于 signed 较大类型和 unsigned 较小类型,如何操作依赖于机器:
前者若能表示后者所有可能值则后者转换为前者,否则都转换为前者的 unsigned 形式
以上两种情况,负值有可能会被转换为 unsigned 导致溢出,造成意料之外的结果
② 转换为指针
ⅰ 表达式中的数组名会自动转换为指向数组首元素的指针
但以下情况除外:
a数组作为取地址操作符&的操作数
b数组作为 sizeof 操作符的操作数
c使用数组对数组的引用初始化时
ⅱ 指向任意类型对象的指针都可转换为 void* 型
ⅲ 整型常量0可转换为任意指针类型
bool 类型转换
ⅰ 算术值和指针值转换为 bool
0false0true
bool 值转换为算术类型
true1 false0
④ 枚举成员转换为整型
枚举类型对象或枚举成员可自动转换为整型(具体类型依赖于机器和枚举成员最大值,但至少为int)
⑤ 转换为 const 对象
const 对象在初始化相关的 const 型引用时自动转换为 const
const 对象的地址或指向非 const 对象的指针也可转换为指向 const 对象的指针
⒉ 显式类型转换
⑴ 若可能,避免使用强制类型转换
确实需要使用时也应尽量小心
⑵ 命名的强制类型转换
dynamic_cast
用于运行时类型识别(RTTI)
const_cast
可以添加或去除指针、数据成员指针或引用的 const 特性
static_cast
ⅰ 可以显式完成编译器隐式执行的任何类型转换
因潜在精度损失产生的编译器警告会被关闭
· 可以用以避免不必要的隐式转换,如
已知 d 为 double, i 为 int, 将 i*=d; 写成 i*=static_cast<int>(d); 可省去将 i 转换为 double 这一非必要步骤
reinterpret_cast
为操作数的位模式提供较低层次的重新解释,结果依赖编译器和机器
任何不当使用都可能导致运行时错误
⑶ 旧式强制转换
在合法使用 static_castconst_cast 的地方,提供与命名强制转换一样的功能
若两种转换均不合法,就执行 reinterpret_cast
由于不易判别每个显式转换的潜在风险,不推荐使用


Tags:,

C++ Primer 读书笔记 – 第四章

Filed Under (新长征路上的代码) by 逆铭 on 2008-08-20 01:17

[笔记索引]

第4章 数组和指针
现代C++程序应使用vector和迭代器代替数组和指针,除非前二者不满足对效率的特殊要求

⒋⒈ 数组
⒈ 数组是由类型说明符、标识符和维度组成的复合类型,能保存一组某种类型的未命名对象
⒉ 定义和初始化
⑴ 类型说明符规定了存放于数组中元素的类型
可使用除引用外的任意类型,包括数组本身(数组的数组即多维数组)
⑵ 维数指定数组中包含的元素个数,须用值大于等于
1的常量表达式定义,一经指定不可改变
⑶ 可以使用初值列表(用花括号括起的一组用逗号分隔的初值,可为空)显式提供元素的初值
① 此时可不指定维数,数组长度将由初值列表中的元素个数自动确定
② 若指定维数:
ⅰ 维数值不能小于初值个数
ⅱ 若维数值大于初始化列表中提供的元素个数,则只用初值列表初始化数组中前面对应的元素
其余元素,若为内置类型则初始化为0,若为类类型则调用默认构造函数
③ 对于多维数组,除第一维外其余维数都须显示指定
初值列表可内嵌花括号以指明各初值对应的位置,若不使用内嵌花括号则依次初始化
⑷ 若未提供初值列表,则数组元素像普通变量一样初始化
⑸ 使用字符串字面值初始化字符数组时注意前者结尾隐含的空字符
⒊ 不允许数组直接复制和赋值
⒋ 数组元素可通过下标操作符[]访问,下标从0开始
下标越界将导致运行时错误

⒋⒉ 指针
⒈ 指针是一种能储存对象地址的对象
⒉ 定义和初始化
⑴ 形式: 类型名* 标识符;
类型名指定指针指向对象的类型(类型名为 void 时指针可指向任意类型的对象)
① 连续声明多个指针时,每个标识符前都要加*号
② 可以连续使用多个星号*表示指向指针的指针
const 限定符
ⅰ 指向 const 对象的指针
a形式: const 类型名* 标识符;
(或 类型名 const* 标识符;)
const 对象只能与这种指针关联
c不能通过这种指针修改所指对象,无论所指是否为 const
由于有此特性,该种指针常用作函数形参以防止所指对象被意外修改
const 指针
a形式:类型名* const 标识符
b指针本身的值不能修改,即不能改变指向,但可以改变所指对象的值
ⅲ 指向 const 对象的 const 指针
a形式: const 类型名* const 标识符
b既不能改变所指对象的值,也不能改变指向
typedef T* pT; 则 const pT t 和 pT const t 均与 T* const t 等价
④ 指向数组的指针
ⅰ 形式: 类型名 (*标识符)[维数][维数]..[维数];
typedef 类型名新类型名[维数][维数]..[维数];
得到数组类型的别名,可以此简化定义
⑵ 应避免使用未初始化的指针
而一个有效的指针必然为以下三种状态之一:
① 保存某确定对象的地址
② 指向一个对象的下一位置
③ 值为0
因此在对指针初始化和赋值时只能使用:
① 值为0的常量表达式
② 类型匹配的对象的地址(使用取地址操作符&获得,该运算符只能对左值使用)
③ 另一对象下一位置的地址
④ 同类型另一有效指针
⒊ 指针操作
⑴ 使用解引用操作符*可以获得指针指向对象的左值,从而操作所指对象
而对指针直接进行赋值等操作将改变指针本身的值,使指针指向另一对象
⑵ 指针可被当作数组的迭代器,用以访问数组元素
① 指针的算术操作
ⅰ 指针与整型值相加(减),得到指向所指元素向后(前)移动相应位置的数组元素的新指针
ⅱ 两个指向同一数组中元素的指针(含超出末端指针)相减,得到一个 ptrdiff_t 型(定义在头文件 cstddef 中, signed 整型)结果,表示两指针所指元素间的距离
② 对指针进行下标操作[],返回所指元素向后(前)移动相应位置的数组元素引用
③ 可以计算超出末端指针,但不允许对其进行解引用操作
计算越界指针也是非法的
void* 指针不支持以上操作

⒋⒊ C风格字符串
⒈ C风格字符串是以空字符结尾的字符数组
字符串字面值就是其实例
⒉ C风格字符串的标准库函数(头文件 cstring)
传给以下库函数的须为指向以空字符结尾的字符数组的非零指针
size_t strlen( char *str );
返回字符串长度(不包括结尾的空字符)
int strcmp( const char *str1, const char *str2 );
比较两个字符串,如果前者大于后者返回正数,小于返回负数,等于返回0
注:不能使用>,<,==操作符比较,它们只会比较存放地址而不会比较字符串
char *strcat( char *str1, const char *str2 );
将str2连接到str1后面,并返回str1
此函数不进行越界检查
char *strcpy( char *to, const char *from );
将str2复制到str1字符串,并返回str1
此函数不进行越界检查
char *strncat( char *str1, const char *str2, size_t count );
将str2的前至多count个字符连接到str1后面,并返回str1
如果越界则截断字符串,因此比strcat安全
char *strncpy( char *to, const char *from, size_t count );
将str2的前至多count个字符复制到str1,并返回str1
如果越界则截断字符串,因此比strcpy安全
⒊ 对于大部分程序而言,标准库类型 string 无论安全性还是效率均强过C风格字符串,因此应尽可能使用 string


Tags:,

C++ Primer 读书笔记 – 第三章

Filed Under (新长征路上的代码) by 逆铭 on 2008-08-12 23:05

[笔记索引]

第3章 标准库类型

⒊⒌ 标准库bitset类型
[以下下标和位数均为 size_t 型(定义在头文件 cstddef 中, unsigned 整型)]
⒈ bitset 是一种类模板,用于保存位集,并提供测位和置位操作
⒉ 定义和初始化
bitset 在定义时需要以常量表达式的形式提供位数N
初始化时参数提供位数不足则剩余高阶位置为
0,位数过多则抛弃多余高阶位
⑴ bitset<N> b;
默认构造函数置各位为0
⑵ bitset<N> b( unsigned long u );
使用 unsigned long 值u的二进制形式初始化
⑶ bitset<N> b( string s, size_t pos=0 );
bitset<N> b( string s, size_t pos, size_t m );
使用 string 对象s或其(下标pos起到结尾或长度为m的)子串初始化
(子)串最右端对应低阶(low-order)位,向左依次类推
⒊ 操作
除支持所有内置位运算符以及==和!=外,还支持以下操作:
⑴ 访问整个 bitset 对象
bool any();
返回是否各位不全为0
bool none();
返回是否各位全为0
size_t count();
返回为1的位的个数
size_t size();
返回能容纳的位数
⑵ 访问 bitset 对象中的位
(从低阶位起,各位编号依次为 0, 1, 2, 3 …)
① 下标操作符[]返回指定位的引用
bool test( size_t pos );
返回指定位的值
③ bitset<N>& set( size_t pos, int val=1 );
设定指定位的值并返回对象的引用
④ bitset<N>& reset( size_t pos );
清零指定位并返回对象的引用
⑤ bitset<N>& flip( size_t pos );
取反特定位并返回对象的引用
也可以b[pos].flip(),返回指定位的引用
⑶ 设置整个 bitset 对象
① bitset<N>& set();
置所有位为1并返回对象的引用
② bitset<N>& reset();
清零所有位并返回对象的引用
③ bitset<N>& flip();
取反所有位并返回对象的引用
⑷ 获取 bitset 对象的值
unsigned long to_ulong();
返回位模式相同的 unsigned long
若越界则产生运行时异常
② string to_string();
返回字符串形式
③ 可使用输出操作符打印 bitset 对象


Tags:,
Get Adobe Flash playerPlugin by wpburn.com wordpress themes

Search:

Codes, Notes & Scribbles Rss