友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
Java编程思想第4版[中文版](PDF格式)-第133部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
l。setText(e。toString());
}
}
else return super。action(evt; arg);
return true;
}
} ///:~
URL 类的最大的特点就是有效地保护了我们的安全。可以同一个Web 服务器建立连接,毋需知道幕后的任何
东西。
15。6。3 用 C++ 写的 CGI 程序
经过前面的学习,大家应该能够根据例子用ANSI C 为自己的服务器写出CGI 程序。之所以选用 ANSI C,是
因为它几乎随处可见,是最流行的 C 语言标准。当然,现在的 C++也非常流行了,特别是采用GNU C++编译器
(g++)形式的那一些(注释④)。可从网上许多地方免费下载g++,而且可选用几乎所有平台的版本(通常
与Linux 那样的操作系统配套提供,且已预先安装好)。正如大家即将看到的那样,从 CGI 程序可获得面向
对象程序设计的许多好处。
④:GNU 的全称是“Gnu's Not Unix”。这最早是由“自由软件基金会”(FSF)负责开发的一个项目,致力
于用一个免费的版本取代原有的Unix 操作系统。现在的 Linux 似乎正在做前人没有做到的事情。但 GNU 工具
在Linux 的开发中扮演了至关重要的角色。事实上,Linux 的整套软件包附带了数量非常多的 GNU 组件。
为避免第一次就提出过多的新概念,这个程序并未打算成为一个“纯”C++程序;有些代码是用普通C 写成
的——尽管还可选用C++的一些替用形式。但这并不是个突出的问题,因为该程序用C++制作最大的好处就是
能够创建类。在解析CGI 信息的时候,由于我们最关心的是字段的“名称/值”对,所以要用一个类
(Pair )来代表单个名称/值对;另一个类(CGI_vector)则将CGI 字串自动解析到它会容纳的Pair 对象里
(作为一个vector),这样即可在有空的时候把每个Pair (对)都取出来。
这个程序同时也非常有趣,因为它演示了 C++与Java 相比的许多优缺点。大家会看到一些相似的东西;比如
class 关键字。访问控制使用的是完全相同的关键字public 和private,但用法却有所不同。它们控制的是
一个块,而非单个方法或字段(也就是说,如果指定private: ,后续的每个定义都具有private 属性,直到
我们再指定 public:为止)。另外在创建一个类的时候,所有定义都自动默认为private。
在这儿使用 C++的一个原因是要利用C++ “标准模板库”(STL)提供的便利。至少,STL 包含了一个 vector
类。这是一个C++模板,可在编译期间进行配置,令其只容纳一种特定类型的对象(这里是 Pair 对象)。和
Java 的Vector 不同,如果我们试图将除Pair 对象之外的任何东西置入 vector,C++的vector 模板都会造成
一个编译期错误;而Java 的Vector 能够照单全收。而且从 vector 里取出什么东西的时候,它会自动成为一
个Pair 对象,毋需进行造型处理。所以检查在编译期进行,这使程序显得更为“健壮”。此外,程序的运行
速度也可以加快,因为没有必要进行运行期间的造型。vector 也会过载operator'',所以可以利用非常方便
的语法来提取Pair 对象。vector 模板将在CGI_vector 创建时使用;在那时,大家就可以体会到如此简短的
一个定义居然蕴藏有那么巨大的能量。
若提到缺点,就一定不要忘记 Pair 在下列代码中定义时的复杂程度。与我们在 Java 代码中看到的相比,
Pair 的方法定义要多得多。这是由于C++的程序员必须提前知道如何用副本构建器控制复制过程,而且要用
过载的 operator=完成赋值。正如第 12 章解释的那样,我们有时也要在Java 中考虑同样的事情。但在C++
中,几乎一刻都不能放松对这些问题的关注。
这个项目首先创建一个可以重复使用的部分,由C++头文件中的 Pair 和 CGI_vector 构成。从技术角度看,
确实不应把这些东西都塞到一个头文件里。但就目前的例子来说,这样做不会造成任何方面的损害,而且更
具有Java 风格,所以大家阅读理解代码时要显得轻松一些:
566
…………………………………………………………Page 568……………………………………………………………
//: CGITools。h
// Automatically extracts and decodes data
// from CGI GETs and POSTs。 Tested with GNU C++
// (available for most server machines)。
#include
#include // STL vector
using namespace std;
// A class to hold a single name…value pair from
// a CGI query。 CGI_vector holds Pair objects and
// returns them from its operator''。
class Pair {
char* nm;
char* val;
public:
Pair() { nm = val = 0; }
Pair(char* name; char* value) {
// Creates new memory:
nm = decodeURLString(name);
val = decodeURLString(value);
}
const char* name() const { return nm; }
const char* value() const { return val; }
// Test for 〃emptiness〃
bool empty() const {
return (nm == 0) || (val == 0);
}
// Automatic type conversion for boolean test:
operator bool() const {
return (nm != 0) && (val != 0);
}
// The following constructors & destructor are
// necessary for bookkeeping in C++。
// Copy…constructor:
Pair(const Pair& p) {
if(p。nm == 0 || p。val == 0) {
nm = val = 0;
} else {
// Create storage & copy rhs values:
nm = new char'strlen(p。nm) + 1';
strcpy(nm; p。nm);
val = new char'strlen(p。val) + 1';
strcpy(val; p。val);
}
}
// Assignment operator:
Pair& operator=(const Pair& p) {
// Clean up old lvalues:
delete nm;
delete val;
if(p。nm == 0 || p。val == 0) {
567
…………………………………………………………Page 569……………………………………………………………
nm = val = 0;
} else {
// Create storage & copy rhs values:
nm = new char'strlen(p。nm) + 1';
strcpy(nm; p。nm);
val = new char'strlen(p。val) + 1';
strcpy(val; p。val);
}
return *this;
}
~Pair() { // Destructor
delete nm; // 0 value OK
delete val;
}
// If you use this method outide this class;
// you're responsible for calling 'delete' on
// the pointer that's returned:
static char*
decodeURLString(const char* URLstr) {
int len = strlen(URLstr);
char* result = new char'len + 1';
memset(result; len + 1; 0);
for(int i = 0; j = 0; i = 'A')
return (hex & 0xdf) 'A' + 10;
else
return hex '0';
}
};
// Parses any CGI query and turns it
// into an STL vector of Pair objects:
class CGI_vector : public vector {
char* qry;
const char* start; // Save starting position
// Prevent assignment and copy…construction:
void operator=(CGI_vector&);
568
…………………………………………………………Page 570……………………………………………………………
CGI_vector(CGI_vector&);
public:
// const fields must be initialized in the C++
// 〃Constructor initializer list〃:
CGI_vector(char* query) :
start(new char'strlen(query) + 1') {
qry = (char*)start; // Cast to non…const
strcpy(qry; query);
Pair p;
while((p = nextPair()) != 0)
push_back(p);
}
// Destructor:
~CGI_vector() { delete start; }
private:
// Produces name…value pairs from the query
// string。 Returns an empty Pair when there's
// no more query string left:
Pair nextPair() {
char* name = qry;
if(name == 0 || *name == '0')
return Pair(); // End; return null Pair
char* value = strchr(name; '=');
if(value == 0)
return Pair(); // Error; return null Pair
// Null…terminate name; move value to start
// of its set of characters:
*value = '0';
value++;
// Look for end of value; marked by '&':
qry = strchr(value; '&');
if(qry == 0) qry = 〃〃; // Last pair found
else {
*qry = '0'; // Terminate value string
qry++; // Move to next pair
}
return Pair(name; value);
}
}; ///:~
在#include 语句后,可看到有一行是:
using namespace std;
C++中的“命名空间”(Namespace)解决了由Java 的package 负责的一个问题:将库名隐藏起来。std命名
空间引用的是标准C++库,而 vector 就在这个库中,所以这一行是必需的。
Pair 类表面看异常简单,只是容纳了两个(private)字符指针而已——一个用于名字,另一个用于值。默
认构建器将这两个指针简单地设为零。这是由于在 C++中,对象的内存不会自动置零。第二个构建器调用方
法 decodeURLString(),在新分配的堆内存中生成一个解码过后的字串。这个内存区域必须由对象负责管理
及清除,这与“破坏器”中见到的相同。name()和 value()方法为相关的字段产生只读指针。利用 empty()方
法,我们查询Pair 对象它的某个字段是否为空;返回的结果是一个 bool——C++内建的基本布尔数据类型。
operator bool()使用的是 C++ “运算符过载”的一种特殊形式。它允许我们控制自动类型转换。如果有一个
名为p 的Pair 对象,而且在一个本来希望是布尔结果的表达式中使用,比如 if(p){//。。。,那么编译器能辨
别出它有一个Pair,而且需要的是个布尔值,所以自动调用 operator bool(),进行必要的转换。
569
…………………………………………………………Page 571……………………………………………………………
接下来的三个方法属于常规编码,在C++中创建类时必须用到它们。根据C++类采用的所谓“经典形式”,我
们必须定义必要的“原始”构建器,以及一个副本构建器和赋值运算符——operator= (以及破坏器,用于清
除内存)。之所以要作这样的定义,是由于编译器会“默默”地调用它们。在对象传入、传出一个函数的时
候,需要调用副本构建器;而在分配对象时,需要调用赋值运算符。只有真正掌握了副本构建器和赋值运算
符的工作原理,才能在 C++里写出真正“健壮”的类,但这需要需要一个比较艰苦的过程(注释⑤)。
⑤:我的《Thinking in C++》(Prentice…Hall;1995)用了一整章的地方来讨论这个主题。若需更多的帮
助,请务必看看那一章。
只要将一个对象按值传入或传出函数,就会自动调用副本构建器Pair(const Pair&)。也就是说,对于准备
为其制作一个完整副本的那个对象,我们不准备在函数框架中传递它的地址。这并不是Java 提供的一个选
项,由于我们只能传递句柄,所以在Java 里没有所谓的副本构建器(如果想制作一个本地副本,可以“克
隆”那个对象——使用 clone(),参见第12 章)。类似地,如果在 Java 里分配一个句柄,它会简单地复
制。但 C++中的赋值意味着整个对象都会复制。在副本构建器中,我们创建新的存储空间,并复制原始数
据。但对于赋值运算符,我们必须在分配新存储空间之前释放老存储空间。我们要见到的也许是 C++类最复
杂的一种情况,但那正是Java 的支持者们论证Java 比C++简单得多的有力证据。在 Java 中,我们可以自由
传递句柄,善后工作则由垃圾收集器负责,所以可以轻松许多。
但事情并没有完。Pair 类为nm 和 val 使用的是char*,最复杂的情况主要是围绕指针展开的。如果用较时髦
的C++ string 类来代替char*,事情就要变得简单得多(当然,并不是所有编译器都提供了对 string 的支
持)。那么,Pair 的第一部分看起来就象下面这样:
class Pair {
string nm;
string val;
public:
Pair() { }
Pair(char* name; char* value) {
nm = decodeURLStrin
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!