2008年10月10日星期五

We Are On The Cruise! -- OnePiece 十周年纪念



财富、名声、力量——曾经拥有这世界上一切男人,海贼王 Gol D. Roger,他在临刑前的一
句话,让全世界的人们趋之若鹜奔向大海:“想要我的财宝吗?想要的话可以全部给你!伟
大航路!我把所有财宝都放在了那里。”于是男子汉们启程前往伟大航路————
大海贼的时代来临了!
----

这是每次星期天晚上都会听到的话,这一天的早上九点,隔海相望的日本将会定期推出日本
漫画《One Piece》。因此这一天来说,对我总是轻松的一天,可以享受周末的轻松,同样
观看《One Piece》——我从来未曾想过自己会爱上一部动画。

而最新的373集openning再次更改,启用了10周年版本主题曲,我们在openning动
画中看到了从第一章节至今出现过的主要人物,从Buggy,Arlong,Smoker到Red Foot Zeff,
巨鲸Laboon,Vivi,Mr.2,还有冰山先生,七武海Sir Crocodile,最新的CP9。这才发现
《One Piece》已经度过漫长的10周年~

One Piece是啥玩意儿呢?对于一些大叔大婶,从来认为动画是小儿科的人们,咱们还是来
普及一下————

One Piece介绍
One Piece是由日本漫画家尾田荣一郎创作的少年漫画,自1997年8月4日起开始在《周刊少
年JUMP》中连载,而漫画每一章节都由株式会社集英社以单行本的方式发行,至今仍在连载。
(OnePiece漫画创刊号)


其名字“One Piece”在故事中为“一个大秘宝”之意。One Piece讲述的是一个17岁的男孩,
Monkey D. Luffy,从误食恶魔果实获得超自然力,而后伙同一群勇敢的海贼的冒险故事,
他们的海贼团体叫做“草帽海贼团”。Luffy以获得世界的终极秘宝——One Piece为目标,从而
成为新一代的海贼王。

我们所听到的译名“海贼王”为台湾中文版发行权转移之后的新代理商“东立出版社”因版权关
系另取的译名。你可以在内地看到以下译名,他都说的是一个动画————One Piece:
海贼王(台湾 大然文化)
航海王(台湾 东立出版社)
海贼王(香港 天下出版社)

借由1亿4千万的漫画销量,One Piece是《周刊少年JUMP》史上销量第三的漫画作品,同样
One Piece也被认为是最受好评的三大漫画作品之一。

情节
听到上面的解说,你还是对One Piece没有概念,因为描述实在乏味,这和打败BOSS拿钥匙
开宝箱的经典剧情没有啥大分别。那我没的办法,因为我无法用三言两语把十年长篇的漫画
说个清楚透彻。

One Piece中描述的海贼,显然和大家想象中的不一样,“独木腿、铁钩手、黑眼罩、三角帽
子、提着酒瓶劫掠四方的人”,这种《加勒比海盗》中的海贼形象在One Piece的世界里显得
更加简单但是多元化,我们的几个主角将会是各自背景不同,使命非凡,勇于追逐梦想,极
富自由精神的,乐于向上,将朋友视为生命的海贼,这将传统意义上作恶多端的海贼形象完
全颠覆。


主线似乎很明了,少年时代的Luffy,被他心目中的英雄“红发Shanks”鼓舞,而起身寻找传
说的大秘宝,这个宝藏可谓是世界财富的集合与象征,更重要的是,它是“海贼王”称号的标
志。为了成为“海贼之王”,Luffy面临的将是世界上最危险,最诡异的海域:伟大航路
(The Grand Line)。

Luffy在东海位置起步,他面临的首要任务是组织一个草帽海贼团;沿着上一代海贼王,
Gold Roger的足迹,他们将一步步的逼近One Piece秘宝。

我喜欢的OP
我在这里说说这部动画的杰出之处。他和其他漫画有啥分别。

1) OP的故事构架太出色了。
在二十几集的时候和三百多集的时候,我依然抱有同样感觉:海贼王的故事写的太好了!
从四海到伟大航路,再到新世界,这样故事的轮廓非常清晰,而四皇,海军,七武海三大势
力的均衡也很直白,尾田没有在这里隐瞒什么。相反观众和读者的想法都很简单:Luffy是
要朝着新世界去的,在三大势力并存的新世界,草帽海贼团显然不是个旁观者。他将会与这
些恶势力搏斗,最终取得One Piece。

传统动画上呢?你几乎永远摸索不到故事的主题,当然如果“主角变强变强再变强”也算主题。
(中将Monkey D. Garp介绍四皇)


2) OP启用了多元化的叙述方式,让故事显得更加具有立体感。
大体轮廓确定之后,尾田先生在漫画的主干上面耗费了太多太多的精力,为我们呈现出来极
其精彩的故事。

我们开始的时候都会知道,Shanks是Luffy的启蒙英雄,由他激励鼓舞,Luffy踏上了海贼王
的征程。那么依据观众的理解,Shanks将来肯定会在伟大航路和Luffy碰面,还会向Luffy提
供帮助和指引。对,没错,不过Shanks后来展现在大家面前的时候还是吓了一跳:他就是在
新世界里君临海上的四皇之一“红发Shanks”。这种落差感,让我们对他们的相遇期待度大增。

尾田的这种起承的手法用了非常非常的多,包括巨鲸Laboon的约定,Zoro对Mihawk的誓言等
等等等。

再来说说,第24集动画的时候,Luffy遭遇Sanji,努力想纳入麾下。但是面对的主要矛盾则
是,摆脱红脚Zeff的干扰和努力说服Sanji,但是红脚Zeff可是穿越伟大航路的大海贼,所
以我们开始把视线聚焦到Luffy vs. Zeff的问题上来。但是后来东海霸主Kreig来袭,我们
又确定他才是本场的强敌。不过更恐怖的才来到我们面前,世界第一剑客,同时是七武海之
一的鹰眼Mihawk紧随Kreig来到东海,而又发生Zoro单挑鹰眼的事件。这让观众才和Zoro一
样惊叹:这才是世界第一的力量!这种层层迭加的紧张感觉,更加白热化的场景,实在让海
贼王更加趋近于电影化。

这样的情节在海贼王屡见不鲜,空岛被迫牵连到本地人的纠纷,司法岛引出Nico Robin离奇
的身世,处处显示出尾田先生的高明。

3) 尾田创作的是一部笑着流泪的漫画。
环顾当今《周年少年JUMP》的三大漫画,OP一定是最为轻松的一个了。Luffy,Usopp和
Chopper本身就是一个超级无敌高效组合……尾田先生在剧情中添加了非常非常多夸张的肢体
语言,可以让猥琐的敌人更加幽默,让恐怖的局势稍显放松。

然而OP努力在人格刻画上费劲心思,Shanks为拯救Luffy而断臂,Zeff为Sanji忍受饥饿,
Laboon厮守和Brook的约定,Vivi和草帽海贼团的惜别之情。这些OP迷们耳熟能详的场面,
每每重复的时候,仍旧能让大家泪流满面。

想到动画开始的时候真的是非常痛苦,不明白是制作成本,还是知名度不高的原因,OP动画
的前几十集真的是朴实到“粗糙”,长时间以来人们都称之为“一流漫画,二流动画”。即便是
到后来,OP仍旧没有引用广泛的3d技术,没有神乎其神的PK情景,绚烂的招式噼里啪啦打上
天昏地暗,我才明白,这才是OP,用真情信义感动大家的海贼王。
(草帽一伙与Vivi惜别)


4) OP的场景和角色非常多元化。
能坚持从头看到今天,我真是对画家佩服的五体投地。十年动画里涌现出来的各式新鲜玩意
儿,实在让人目瞪口呆。

那么那么多的建筑风格,那么那么多的恶魔果实,那么那么多的珍禽异类,那么那么奇怪的
大叔大婶,那么那么搞笑的敌人。。。尾田脑子里天天在想些什么呢?

5) OP自始自终保留了故事的神秘美感,这是能够在十年以来保持长久不衰的重要原因。
当然我在这里就不再讨论七武海是啥能力,四皇最后一位是谁了,那是些论坛上永久不会停
歇的话题(除了尾田直接说出来)。我只说一些重头戏:

历史文本之谜。空岛的老头曾经说过:“20年前也有一个家伙来到空岛,那家伙真是豪爽阿!
”紧接着,在空岛的历史正文旁边,草帽们发现了海贼王Roger的签名。在之后的水之都章节
里,我们已经发现Nico Robin隐藏着巨大的秘密,这显然印证了青稚所言:那个女人身后的
黑暗不是你们可以理解的。司法岛一战才告诉我们,恶魔之子Nico Robin正是可以读懂历史
正文的最后一人。由此即将揭示的空白历史到底描述了什么呢?

还有一些热度非常高的未解之谜:Luffy的父亲Dragon到底在进行什么样的事业,海贼王
Roger投降的原因呢?恶魔果实的来源?

漫画达到10年历程,我们还是被这样的迷题折腾的死去活来,这无疑让One Piece保持着神
秘的美感,延续着它的青春和生命力。
(革命家Dragon一切都是个迷)


6) 最后,轻松搞笑的海贼王,仍旧具有深度和前瞻性。
世界政府掩盖历史,无论如何,这都会让人联想到日本政府掩盖真相的事实。这个隐喻不言
而喻,真正的历史是属于人类的,它们一定不会被抹杀。

在尾田的笔下,人物焕发出来的精神和气质值得当代青年人学习:
这里有Sanji表现出来的骑士精神,Zoro为梦想坚持不懈,永不言放弃,Usopp唯唯诺诺,需
要不断超越自我,Brook作死守誓约的男人,Luffy重视朋友轻易,这一群爱理想的少年焕发
出来的自由注意精神,永远值得我们称道!

对海贼王的问候

最后在十周年之际,向他们致以问候!

Monkey D. Luffy,船长。没啥可说的。


三刀流 Roronoa Zoro,Zoro在OP世界里以传统职业——剑士的身份,通过苦行不断磨练自己,
最终达到终极目标————打败世界第一剑客 Dracule Mihawk!


航海士 Nami,在地理位置复杂,气候恶劣的Grand Line,一位杰出的航海士将会是顺利航
行的关键。清纯可爱的Nami对宝箱可是有着天生的直觉。


狙击手 Usopp,他用来完成远程狙击人物,在尾田的笔下,Usopp性格幽默,但身材柔弱,
胆小怕事,这使得Usopp更加趋近于常人,形象具有警世的价值!
值得一提的是,Usopp的声优是非常出名的山口胜平,你可以想象金田一,犬夜叉,怪盗基
德,乱马……实乃声优界的超级大腕。


厨师 Sanji,在Sanji看来,没有什么比荒岛上饿肚子更加恶劣的体验了。平日里的Sanji,
形象俊朗,见女士腿软,极富“骑士精神”。


医师 Tony Tony Chopper,队中唯一的一位动物角色,Chopper极富仁义的天性和羞怯可爱
的性情,使得他胜任医生重要一职。为其献声的因为皮卡秋誉满全球的大谷育江。


恶魔之子 Nico Robin,通晓古文字的她可以破解“冥王”之谜,而她黑暗离奇的身世将会在
司法岛章节揭开。从Miss All Sunday到草帽海贼团一员,尾田对于Robin花费了太多的手笔,
Nico Robin 也是我个人最喜欢的动漫女性角色。


造船师 Franky,在水之都对草帽海贼团建造新船——万里阳光号,这无疑是给海贼团们进行
了大换血。


音乐家 Brook,拥有88岁的年纪,他将是One Piece的第八个队员。Brook是一个借助恶魔果
实保持生命的骷髅,他有着儒雅的风度,却保留了很龌龊的个性(喜欢问女孩子:可以看一
下内裤吗……)。Brook的目标是绕行伟大航路一周,最后回到双子岬完成50年的约定。


第一只船,黄金梅利号(Going Merry),这是来自Usopp相知相识的朋友Kaya赠送给草帽们
的珍贵礼物。


第二之船,万里阳光号(Thousand Sunny),在司法岛事件之后,Franky送给草帽海贼团一
件大礼,借助自己无以伦比的造船工艺,新船具有更出色的性能,舒适的船舱和强大的功能。


最后是大神尾田荣一郎,现年33岁。他的画风深受著名画家鸟山明的影响。


OP十周年纪念!


-----------------
引用:
1. 少年JUMP官方网站
2. wiki -- onepiece
3. The Rise and Fall of Weekly Shonen Jump:
4. OnePiece官方网站

2008年10月6日星期一

[TIC++] C9. Inline Functions

代码阅读 <Thinking in C++>
Chapter 9. Inline Functions

一、什么是Inline

// c09: macro_side_effects.cpp
#include <fstream>
using namespace std;

#define BAND(x) (((x) > 5 && (x) < 10) ? (x) : 0)

int main()
{
ofstream out("macro.out");
// assure(out, "macro.out");
for (int i = 4; i < 11; i++) {
int a = i;
out << "a = " << a << endl << "\t";
out << "BAND(++a)" << BAND(++a) << endl;
out << "\t a = " << a << endl;
}

}
/* macro.out:
a = 4
BAND(++a)0
a = 5
a = 5
BAND(++a)8
a = 8
a = 6
BAND(++a)9
a = 9
a = 7
BAND(++a)10
a = 10
a = 8
BAND(++a)0
a = 10
a = 9
BAND(++a)0
a = 11
a = 10
BAND(++a)0
a = 12
*/

第一个例子,介绍了宏(macro)带来的负面效应。BAND(x)定义了一个三元运算符,C/C++中
会对选择语句中的与运算和或运算进行优化,(x)>5为false之后,将不会考虑(x)<10,而自
增运算符结合macro会产生预料之外的效果。

为了消除奇异,避免此类情况发生,我们引入了内联方法——

// c09: inline.cpp
#include <iostream>
#include <string>
using namespace std;

class Point {
int i, j, k;
public:
Point(): i(0), j(0), k(0) {}
Point(int ii, int jj, int kk)
: i(ii), j(jj), k(kk) {}
void print(const string &msg = "") const
{
if (msg.size() != 0)
cout << msg << endl;
cout << "i = " << i << ","
<< "j = " << j << ","
<< "k = " << k << endl;
}
};

int main()
{
Point p, q(1,2,3);
p.print("value of p");
q.print("value of q");
}

这里和以前并没有什么异常,这证明人的习惯是很可怕的……我们要说的是,以前在类中定义
的所有函数,都是内联函数(inline functions)。内联函数也只是存在于编译期内,编译的
时候,编译器会打开内联函数的代码,直接迭代到调用处。他起到了宏的作用,而且避免了
负面效用。

考虑一下print(const string&)方法,如果没有内联,那么print代码本身就要将this指针
放入堆栈,并且使用一次汇编语句CALL,大多数机器中,这段代码的长度会比内联展开的代
码量要大,而且花费时间也会更长。

二、使用内联
这个例子包含了三个代码段stash4.h,stash4.cpp,stash4test.cpp。

stash4.h

// c09: stash4.h
// inline functions

#ifndef __STASH4_H
#define __STASH4_H

class stash {
int size;
int quantity;
int next;
unsigned char *storage;
void inflate(int increase);
public:
stash(int sz): size(sz), quantity(0), next(0), storage(0) {}
stash(int sz, int init_quantity): size(sz), quantity(0), next(0), storage(0)
{
inflate(init_quantity);
}

~stash()
{
if (storage != 0)
delete []storage;
}

void *fetch(int index) const
{
if (index >= next)
return 0;
return &(storage[index * size]);
}

// declarations of non-inline functions
int add(void *element);
int count() const { return next; }
};


#endif // __STASH4_H

stash4.cpp

// c09: stash4.cpp
#include "stash4.h"
#include <iostream>
#include <cassert>
using namespace std;

const int increment = 100;

int stash::add(void *element)
{
if (next >= quantity)
inflate(increment);
int start_bytes = next * size;
unsigned char *e = (unsigned char*)element;

for (int i = 0; i < size; i++)
storage[start_bytes + i] = e[i];
next ++;
return(next -1);
}


void stash::inflate(int increase)
{
assert(increase >= 0);
if (increase == 0)
return;
int new_quantity = quantity + increase;
int new_bytes = new_quantity * size;
int old_bytes = quantity * size;
unsigned char *b = new unsigned char[new_bytes];

for (int i = 0; i < old_bytes; i++)
b[i] = storage[i];
delete [](storage);
storage = b;
quantity = new_quantity;
}
///:~

stash4test.cpp

// c09: stash4test.cpp
#include "stash4.h"
#include <fstream>
#include <iostream>
#include <string>
using namespace std;

int main()
{
stash int_stash(sizeof(int));

for (int i = 0; i < 100; i++)
int_stash.add(&i);
for (int j = 0; j < int_stash.count(); j++)
cout << "int_stash.fetch(" << j << ") = "
<< *(int*)int_stash.fetch(j)
<< endl;

const int bufsize = 80;
stash string_stash(sizeof(char) * bufsize, 100);
ifstream in("stash4test.cpp");
string line;
while(getline(in, line))
string_stash.add((char*)line.c_str());

int k = 0;
char *cp;
while ((cp = (char*)string_stash.fetch(k++)) != 0)
cout << "string_stash.fetch(" << k << ") = "
<< cp << endl;
}
/* result: 结果中途省略
int_stash.fetch(0) = 0
int_stash.fetch(1) = 1
int_stash.fetch(2) = 2
int_stash.fetch(3) = 3
...
int_stash.fetch(98) = 98
int_stash.fetch(99) = 99
string_stash.fetch(1) = // c09: stash4test.cpp
string_stash.fetch(2) = #include "stash4.h"
string_stash.fetch(3) = #include <fstream>
string_stash.fetch(4) = #include <iostream>
string_stash.fetch(5) = #include <string>
string_stash.fetch(6) = using namespace std;
string_stash.fetch(7) =
string_stash.fetch(8) = int main()
string_stash.fetch(9) = {
...
string_stash.fetch(31) = }
*/

stash中的简单方法在这里用到了inline,他们更加的高效和简洁。但也要留意两个最大的
方法在外部被定义,这样他们就不是inline方法,因为这样作不会带来任何效益。

三、向前引用特性

// c09: evaluation_order.cpp
// inline evaluation order

class forward {
int i;
public:
forward(): i(0) {}
// call undeclared function
int f() const { return g() + 1; }
int g() const { return 1; }
};

int main()
{
forward fwd;
fwd.f();
}

注意到,f()在g()声明或者定义之前调用了g(),这在inline函数世界里变得可能了。
_

2008年10月5日星期日

[TIC++] C8. Constants

代码阅读<Thinking in C++>
Chapter 8. constants

一、介绍常量

//safecons.cpp
#include <iostream>
using namespace std;

const int i = 100;
const int j = i + 10;
long address = (long)&j;
char buf[j + 10];

int main(void)
{
cout << "type a character & CR: ";
const char c = cin.get();
const char c2 = c + 'a';
cout << c2 << endl;
}

1. 宏,它只存在于预编译期(preprocessing),不占据内存空间,不包含类型信息。
2. 常量,也在编译期被翻译。通常情况下默认为内部链接,并且不会为const分配内存。这
被称为const folding原则。但是这个原则在很多情况下会被打破。
特例有很多,主要一点就是常量的外部引用。比如extern声明会强制建立外部链接,这
要求修饰的const也要提供地址(内存空间所在位置)。

二、常量遭遇指针

// c08: const_pointer.cpp
const int *u; // pointer to const int, init not required
int const *v; // still pointer to const int!
int d = 1;
int * const w = &d; // const pointer
const int * const x = &d; // const pointer to const object
int const * const x2 = &d;

int main(void){}

指针也可以被声明常量,这里带来两个歧义:一个是指针指向的东西被声明常量(pointer
to const);另一个是指针包含的地址本身被定义为常量(const pointer)。
上面的例子就列举了两种定义,还有另外指向常量的常量指针。
1) 指向常量的指针(`const*')
可以不被初始化
2) 常量型指针(`*const')
必须初始化
3) 指向常量的常量指针(`const * const')
必须初始化


// c08: pointer_assignment.cpp
int d = 1;
const int e = 2;
int *u = &d;
//! int *v = &e; // illegal -- a const
int *w = (int*)&e; // legal but bad practice

int main(){}

这里举例说明了指针赋值。
1. 常量不可以再被非常量直接引用(指针和引用均不可以)。
2. 但是他可以被强制转换,转换的安全性需要程序员负责。
3. 不可以在文件域内部对指向常量指针赋值(p2c,cp都不行)

三、常量返回值 return consts by value

// c08: constval.cpp
// return consts by value
// has no meaning for built-in type
int f3() { return 1;}
const int f4() { return 1;}

int main(void)
{
const int j = f3(); // Works fine
int k = f4(); // But this works fine too!
}

原子类型之间的赋值,只是拷贝,勿需考虑常量情况。


// c08: const_return_values.cpp
// const return by value
// result cannot be used as an lvalue

class X {
int i;
public:
X(int ii = 0);
void modify();
};

X::X(int ii)
{
i = ii;
}

void X::modify()
{
i++;
}

X f5()
{
return X();
}

const X f6()
{
return X();
}

void f7(const X& x) // pass non-const reference
{
x.modify();
}

int main()
{
f5() = X(1);
f5().modify();
// compile-time error
//! f6() = X(1);
//! f6().modify();
//! f7(f5());
//! f7(f6()); // 29
}

将返回值定义为常量,这对于原子类型来说没有意义。而在自定义类型中,却有很大不同。
NOTE!
如果函数返回一个常量类型的类对象,那么返回的值将不能成为左值(不可被定义和修改)。
而原子类型本身就不能成为左值,所以返回常量原子类型是无意义的。
f5()返回了非常量型,而f6()返回的是常量型的类对象,因此f6()的赋值和修改都是非法的
(f6不可以作为左值)。

四、传递和返回地址 passing and returning addresses

// c08: const_pointer2.cpp

void t(int*) {}

void u(const int *cip)
{
//! *cip = 2; // illegal -- modifies value
int i = *cip; // OK -- copies value
//! int *ip2 = cip; // illegal: non-const
}

const char *v()
{
// return address of a static array
return "result of function v()";
}

const int * const w()
{
static int i;
return &i;
}

int main()
{
int x = 0;
int *ip = &x;
const int *cip = &x;

// passing addresses
t(ip); // non-const* to non-const*
//! t(cip); // const* to non-const*
u(ip); // non-const* to const*
u(cip); // const* to const*

// returning addresses
//! char *cp = v(); // const* to non-const*, not OK
const char * ccp = v(); // const* to const*
//! int *ip2 = w(); // const* to non-const*, not OK!
const int * const ccip = w(); // const* to const*
const int * cip2 = w();
//! *w() = 1; // modify consts, not OK
}

无论是传递地址,还是返回地址。从const*到non-const*的传递(或赋值)都是被禁止的,
而如果选择将const转换为non-const,则需要确保转换的安全性。

五、常量成员的初始化

// c08: const_init.cpp
#include <iostream>
using namespace std;

class Fred {
const int size;
public:
Fred(int sz);
void print();
};

Fred::Fred(int sz) : size(sz) {}
void Fred::print() { cout << size << endl; }

int main()
{
Fred a(1), b(2), c(3);

a.print(), b.print(), c.print();
}

这里展示了构造方法的初始化列表的用法。形如:
Fred::Fred(int sz) : size(sz) {}
函数列表括弧后的单位就是,常量初始化的地方。唯有这里可以将类中定义的常量成员初始
化,实际上,他们是在进入函数体之前就进行了初始化。

六、const 成员遭遇 static

// c08: string_stack.cpp
// using static const to create a compile-time constant
// inside a class
#include <cstring>
#include <string>
#include <iostream>
using namespace std;

class string_stack {
static const int size = 100; // a static constant
const string *stack[size];
int index;
public:
string_stack();
void push(const string *s);
const string *pop();
};

string_stack::string_stack():index(0)
{
memset(stack, 0, size * sizeof(string*));
}

void string_stack::push(const string *s)
{
if (index < size)
stack[index++] = s;
}

const string *string_stack::pop()
{
if (index > 0) {
const string *rv = stack[--index];
stack[index] = 0;
return rv;
}
return 0;
}

const string icecream[] = {
"pralines & cream",
"fudge ripple",
"jamocha almond fudge",
"wild mountain blackberry",
"raspberry sortbet",
"lemon swirl",
"rocky road",
"deep chocolate fudge"
};

const int icsz = sizeof(icecream) / sizeof(*icecream);

int main()
{
string_stack ss;
for(int i = 0; i < icsz; i++)
ss.push(&icecream[i]);
const string *cp;
while((cp = ss.pop()) != 0)
cout << *cp << endl;
}

这里展示了C++ string的强大与方便。但是更重要的是,解释了compile-time constants的
用法。
static类成员意味着,无论用此类建立多少个对象,static成员实例都只会存在一个。
另外static const的特性要求,static const常量(原子型)必须在定义位置初始化。

从例子中可以看到,push持有const string*参数,pop()则返回const string*,还有
string_stack包含了一个const string*成员。这看来像一个锁链一样,一环套一环。
stack_stack包含const string *stack[],这要求在pop():
* 中间赋值必须用const传递, const string *rv;
* 返回值类型必须是const.

const string *icecream也要求push():
* 持有const参数

三个const约定了字符串以const形式亚栈,而除了push和pop以外,你再也无法更改
成员*stack[]。

七、const 成员方法

// c08: const_member.cpp
class X {
int i;
public:
X(int ii);
int f() const; // const member func
int g();
};


X::X(int ii): i(ii) {}
int X::f() const { return i;}
int X::g() {}

int main()
{
X x1(10);
const X x2(20);

x1.f();
x2.f();
//! x2.g(); // discards qualifiers
}

const成员方法,将会告诉编译器,他可以被常量对象调用。而没有被申明常量的方法,将
不可以被常量对象调用!

const成员方法和const函数(const func)有本质不同:
* const func 是返回一个常量值。
* func const 则是表明此成员方法可以被常量对象调用。他的使用方法是:

为了强调const方法,编译器强迫在const成员方法的声明和定义处都要加上const。

接下来细细观察const成员方法和普通成员方法的不同——


// c08: quoter.cpp
// random quote selection

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

class quoter {
int lastquote;
public:
quoter();
int get_lastquote() const; // const member func
const char *quote(); // member func which returns a const pointer
};

quoter::quoter()
{
lastquote = -1;
srand(time(0));
}

int quoter::get_lastquote() const
{
return lastquote;
}

const char *quoter::quote()
{
static const char *quotes[] = {
"Are we having fun yet?",
"Doctors always know best",
"Is it ... Atomic?",
"Fear is obscene",
"There is scientific evidence "
"to support the idea "
"that life is serious",
"Things that make us happy, make us wise",
};
const int qsize = sizeof quotes / sizeof *quotes;
int qnum = rand() % qsize;

while (lastquote >=0 && qnum == lastquote)
qnum = rand() % qsize;
return quotes[lastquote = qnum];
}

int main()
{
quoter q;
const quoter cq;

cq.get_lastquote();
//! cq.quote(); // 常量型对象无法调用非常量型成员方法
for (int i = 0; i < 20; i++)
cout << q.quote() << endl;
}

这里比较了const对象和普通对象处理的区别,另外揭示了const成员方法的条件:
如果成员方法体内更改了数据成员,那么它就不能声明为const。

比如const char *quoter::quote()更改了成员 lastquote(return语句),它将不能声明
为const;而int quoter::get_lastquote()则没有动,他就可以成为const成员方法,而且
可以被常量对象cq安全调用。

如果我确实声明了const成员方法,我又确实想更改类成员,我该如何是好?

八、bitwise vs. logical const

// c08: castaway.cpp
class Y {
int i;
public:
Y();
void f() const;
};

Y::Y(): i(0) {}

void Y::f() const
{
//! i++;
((Y*)this)->i++;
// Better: use C++ explicit cast syntax:
(const_cast<Y*>(this))->i++;
}

int main()
{
const Y yy;
yy.f();
}

bitwise const意思是说,此对象的每一数据比特都是永久不可变的,这样它的任何一部分
都不可改变;而logical const意思是,虽然从概念上说整个对象不可改变,但是可以从成
员基础上更改它本身。
这其实是编译器给const定义的两面性,一方面编译器小心翼翼地确保const对象是bitwise
的,另一方面,它又提供两个路径来通过_const成员函数_来更改数据成员。

以上就是第一个:转换掉const属性。无论是C还是C++语法,都一样可行。
这样带来的一个问题就是,你无法确认你的更改是否有效。。。

于是提供了第二个方法:


// c08: mutable.cpp
class Z {
private:
int i;
mutable int j;
public:
Z();
void f() const;
};

Z::Z(): i(0), j(0) {}

void Z::f() const
{
//! i++; // error - const mem func
j++;
}

int main()
{
const Z zz;
zz.f();
}

mutable 关键词告诉编译器,被定义为mutable的数据成员,在const成员方法内仍旧可以更
改。相对转型,显然mutable更加优雅一点。

TIC++也提到了只读属性(ROMability),如何指导编译器把const对象放到只读内存中呢?
这个条件比较复杂,任何可以改写数据成员的可能都会阻止只读化:
1) 不包含logical const特性(不能有mutable关键词,也不能有const转型)
2) class或者struct不可以包含任何自定义构造和析构方法
3) 也不可以包含有自定义构造和析构方法的超类和成员对象(子类没有限制)

九、volatile和类

// c08: volatile.cpp

class comm {
const volatile unsigned char byte;
volatile unsigned char flag;
enum { bufsize = 100 };
unsigned char buf[bufsize];
int index;
public:
comm();
void isr() volatile;
char read(int index) const;
};

comm::comm(): index(0), byte(0), flag(0) {}

// only a demo; won't actually work as an interrupt service routine:
void comm::isr() volatile
{
flag = 0;
buf[index++] = byte;
if (index >= bufsize)
index = 0;
}

char comm::read(int index) const
{
if (index < 0 || index >= bufsize)
return 0;
return buf[index];
}

int main()
{
volatile comm port;
port.isr(); // OK
//! port.read(0); // Error, read() not volatile
}

volatile关键字和const是极其相似的修饰词,只不过他们的意义不同。
通常情况下,编译器会告诉我们:我把此数据读入到了寄存器,但我保证不会再去碰它。下
次需要读取此变量,直接从寄存器中读取即可(而不需要到内存中寻找)。
而volatile告诉编译器:这个数据成员的活动,将会超过编译器的理解范围。如果需要读取
此数据,不要尝试从寄存器中取值,因为内存和寄存器可能已经出现了不同步。

同const一样,volatile对象也只能调用volatile成员方法。

十、总结
const一章的例子比其他章节要多,原因是const类型在C++中遇到了多种繁杂的情况,而且
他们也表现出来各自的特性。总结来说,我们探讨了以下情况:

* 原子类型申明为const(这里也讨论了const*和*const)
const声明意味着,这意味着该内存为只读属性。const存在于编译期,通常情况下默认为
内部链接,并且不会为const分配内存,这被称为const folding原则。const类型的局部
标识符必须在定义时候初始化。
const指针包括了指向常量的指针(const*)和常指针(*const),const*可以选择是否初始
化,但是他不可以显性修改指针指向的内存(*x = y illegal);*const不可以修改指针
本身((int*)a = (int*)b illegal)。

* 返回值为const
const返回值将不可作为左值,赋值修改都是非法的。而作为右值时候,const返回值的左
值必须为const,const到non-const的传递和赋值都是非法的。

* 传递参数为const
const到non-const的传递仍旧是非法的。要切记这一规则。

* 成员数据为const
const成员数据不可以在普通成员方法中更改。这里也给出了特例(const_init.cpp):
使用构造方法的初始化列表来完成常量数据成员的初始化。

* 成员数据为static const
static说明所有的成员实例都会共享一个static数据成员。static const额外要求必须在
定义位置实现初始化。

* 成员方法为const
首先,一个const成员方法将永远不会更改类的数据成员。
再次,声明为const的成员方法允许被const对象调用,而普通成员方法是不允许调用的。

* 对象为const
const对象表现出两个特性,一个是表面上他的整体不可改变(bitwise const),另一个
则是通过某些特殊方法实现更改(logical const)。
logical const的实现有两种方法,一个在const成员方法使用强制类型转换
(castaway.cpp),另一个是在类定义中给想要修改的值添加mutable修饰词
(mutable.cpp)。
_

2008年10月4日星期六

[TIC++] C7. Function Overloading and Default Arguments

代码阅读<Thinking in C++>
Chapter 7. Function Overloading and Default Arguments

一、C++中的union

//unionclass.cpp
#include
using namespace std;

union u {
private:
int i;
float f;
public:
u(int a);
u(float b);
~u();
int read_int();
float read_float();
};

u::u(int a)
{
i = a;
}

u::u(float b)
{
f = b;
}

u::~u(void)
{
cout << "u::~u()\n"; } int u::read_int(void) { return i; } float u::read_float(void) { return f; } int main(void) { u X(12), Y(1.9F); cout <<>

这个例子展示了union的用法。union仍就是C++的关键词,union也和class一样,他也可以
有constructor,destructor,成员方法甚至访问控制。
union和class的不同之处在于:
1. union含有独特的数据存储方式。
2. union不能被继承,这也是由特性1决定的。这使得它在C++里无法大展身手。

二、匿名union

//supervar.cpp
#include
using namespace std;

class supervar {
enum {
character,
integer,
floating_point,
} vartype;
union { // anonymous union
char c;
int i;
float f;
};
public:
supervar(char ch);
supervar(int ii);
supervar(float ff);
void print();
};

supervar::supervar(char ch)
{
vartype = character;
c = ch;
}

supervar::supervar(int ii)
{
vartype = integer;
i = ii;
}

supervar::supervar(float ff)
{
vartype = floating_point;
f = ff;
}

void supervar::print(void)
{
switch (vartype) {
case character:
cout << "character: " <<>

1. class supervar定义了一个enum类型,但是没有给此类型标注类型名。因为他只是用此
enum定义了一个变量实例vartype.
2. class supervar定义了一个union类型成员,这是一个匿名union(anonymous union),这
样在此类的成员中应用union成员,将不必使用成员操作符`.'。
如果匿名union定义在文件类型,_一定_要声明为static,这样他就有了内部链接。
_

2008年10月2日星期四

国庆第二天-疯狂的南山巡游

国庆到达第二天了,没有能出去爬山,也没有太多能力腐败了。想起酝酿许久的自行车旅
行,觉得今天真是个好日子。曾胖花费¥1400的自行车,多好的条件,于是立刻出发!

地图路线:
http://maps.google.com/maps/ms?ie=UTF&msa=0&msid=102563878911537749872.0004584479d8054cb6ac0
(注意卫星地图和交通图是不一致的,选择卫星地图)



首先从大新居委会出发,西转到深圳西车站,然后顺着月亮湾大道南下。沿途大部分交通工
具都是货车,在港湾和机场之间来回。不过一会,就来到南山区的西南角——妈湾。


接着从妈湾东转到赤湾,这一带是集装箱盛集的地方。


然后来到赤湾岛部分,伸出的小岛部分被集装箱覆盖。


然后进入到赤湾村,我在深圳不知道曾经坐过多少次226和204,这次就来到他们的终点站——
赤湾。赤湾的景点,最出名的就是宋少帝陵。
引自百度百科:宋少帝陵位于蛇口赤湾,是广东省境内仅有的黄陵。1278年蒙古入侵,南宋
王朝败退至广东新会,爆发"崖海大战".南宋政权军队被歼始尽,危急关头,宰相陆秀夫背
负仅8岁的少帝跳海殉国.少帝陵处还有陆秀夫背帝的石像.


经过兴海大道,蛇口港,终于来到海上世界。
曾经在07年的时候,我曾经赴海上世界参加百度贴吧深圳吧的聚会,认识了大熊,老男人,
大黄等人。跟着一帮人混达了很长时间,现在大家都是常聚的好友了。因此海上世界是一个
非常有意义的地方,让我想起了一年前的那个时侯。


傍晚时候沿着南海大道狂奔。由于夜色降临,路途急忙,没能拍到照片,敬请见谅。今日行
程也告一段落。

最后给第二主角露一小脸。祝愿大家开心!~


_