随笔 - 7  文章 - 15  trackbacks - 0
<2006年6月>
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

常用链接

留言簿(2)

随笔档案(7)

相册

搜索

  •  

积分与排名

  • 积分 - 15575
  • 排名 - 945

最新评论

阅读排行榜

评论排行榜

本文给出了一个对Microsoft .NET 和Microsoft's XML Web服务平台的总体介绍以及使用它们的好处。同时我们还将举例说明 .NET 是如何改变终端用户和商业的计算模式。
  
    在本文中我们不想涉及到很精深的技术,任何稍微懂一点电脑和Internet知识的人都可以理解本篇的内容。Micorosoft还为那些希望掌握
.NET 的公司执行官、IT领导和程序员们
  提供了更多的资源。
  
    什么是Microsoft
.NET ?
  
    Microsoft
.NET 是Microsoft的 XML Web服务平台。 .NET 包含了建立和运行基于 XML 软件 所需要的全部部件。
  
    Microsoft
.NET 解决了下面这些当今 软件 开发中的一些核心问题:
  
    ●互操作性(Interoperability)、集成性(Integration)和应用程序的可扩展性(extensibility)太难实现而且代价很高。Microsoft
.NET 依靠 XML (一个由World Wide Web Consortium(W3C)管理的开放标准)消除了数据共享和 软件 集成的障碍。
  
    ●无数具有相当竞争力的私有
软件 技术使得 软件 的集成变得非常复杂。而Microsoft .NET 建立在一个开放的标准上,它包含了所有 编程 语言。
  
    ●当终端用户使用
软件 时,他们总觉得不够简便。有时甚至感到很沮丧,因为他们无法在程序之间方便地共享数据或是无法对能访问的数据进行操作。 XML 使数据交换变得容易了,并且 .NET 软件 可以使得用户只要一得到数据就能对它们进行操作。
  
    ●终端用户们在使用Web的时候,无法对自己的个人信息和数据进行控制,这导致了个人隐私和安全泄漏问题。而Microsoft
.NET 提供了一套服务,使用户可以管理他们的个人信息,并且控制对这些信息的访问。
  
    ●.COM公司和Web站点开发者们很难为用户们提供足够的有价值的数据,至少有一部分原因是由于他们的应用程序和服务无法很好地和其他程序和服务合作,只是一个不和外界连接的信息孤岛。而Microsoft
.NET 的设计宗旨就是为了使来自于多个站点和公司的数据或服务能够整合起来。
  
    如同MS-DOS和Windows一样,
.NET 将大大改变我们的计算领域。MS-DOS使得个人电脑在商业和家庭中广为接受;Windows增强了用户的图形界面,使其成为首选的与 软件 交互方式,最终使得图形界面成为个人电脑的主流。而 .NET 则要把 XML Web服务变成日后的主流计算模式。
  
    
XML Web服务是建立在 XML 数据交换基础上的 软件 模型,它帮助应用程序、服务和设备一起工作。用 XML 进行共享的数据,彼此之间独立,但同时又能够松耦合地连接到一个执行某特定任务的合作组。
  
    想了解
XML Web服务如何工作,最方便的方法就是把它和拼装 游戏 作比较。 XML Web服务和拼装 游戏 的拼块一样是一些独立的单元。后者用一个标准的方法相互咬合在一起, XML Web服务与这类似,但它是通过 XML message实现相互交互的。当你把拼块拼在一起时,你就得到了一个对象:一幢房子、一艘船或一架飞机。同样,当你把 XML Web服务结合在一起时,你就得到了一个完成某特定任务的 软件 解决方案。同一拼块可以用在很多不同对象中,而一个 XML Web服务同样也可以用在不同的方案组中,作为不同任务解决方案的一个组成部分。
  
    
XML Web服务使开发者能够对他们所要的程序的来源进行选择,可以自己创建或购买程序的功能块;同样也可以选择是让自己的方案使用其他的 XML Web服务,还是让其他的程序使用自己的服务。这意味着一个公司不必为了给客户一个完整的解决方案而不得不提供方案的每一个组成部分。
  
    
XML Web服务除了个服务相互之间独立以外,对访问它们的设备而言也是独立的。与独立应用程序不同的是, XML Web服务并没有束缚于某一特定的 编程 语言或商业应用程序或者是某一在线服务。这给了终端用户足够的自由,使其可以使用任何访问设备,从台式电脑到移动电话都可以。
  
    
.NET 战略
  
    Microsoft
.NET 程序员们设计编写的是 XML Web服务,而不是服务器或客户端的独立应用程序。他们把这些服务组合成松耦合,相互协作的 软件 群, XML Web服务之间使用 XML messaging进行通讯。为了做到这一点,程序员需要:
  
    1.一个
软件 平台,用于建立一种新的完整的个人用户经验。
  
    2.一个
编程 模型和工具,用以建立和整合 XML Web服务。
  
    3.一套能为应用程序和服务提供基础的可
编程 的服务
  
    Microsoft的
.NET 战略就瞄准了这三点。
  
    
.NET 包括:
  
    ●
.NET 平台,这是一套 编程 工具和基本构架,用来创建、发布、管理和整合 XML Web服务
  
    ●
.NET 体验,这是终端用户用以和 .NET 交互的手段

.NET 平台
  
    Microsoft的平台是由用于创建和运行
XML Web服务组成的。它包含了下面四个组件:
  
    
.NET 框架和Visual Studio .NET :这些是开发人员用来生成 XML Web服务的工具。 .NET 框架是Microsoft .NET 平台核心中的一套 编程 接口;Visual Studio .NET 是一套多语言系列的
  
编程 工具。
  
    服务器基本结构(Server Infrastructure):
.NET 的服务器基本结构是一系列用于生成、发布和操作 XML Web服务的基础程序,包括Windows和各种 .NET 企业服务器。主要的技术包括对 XML 、scale-out及跨程序和服务的商务流程(business process orchestration)的支持。这些服务器包括有:
  
    ●Application Center 2000,用于scale-out solutions
  
    ●BizTalk Server 2000,用于创建和管理基于
XML 的跨程序和服务的商务流程(business process orchestration across applications and services)
  
    ●Host Integration Server 2000,用来访问主机上的数据和应用程序
  
    ●Mobile Information 2001 Server,使移动设备,比如移动电话,也能使用这些应用程序
  
    ●
SQL Server 2000储存和检索结构化的XML数据
  
    Building Block Services: Building Block Services是一套以用户为中心的
XML Web服务,它把用户数据的控制权从应用程序移到了用户手上,使Web有了一个翻天覆地的变化,做到了程序、服务和设备之间的简单性及一致性,这保证了所有的交易都必须得到用户的同意。这些服务包含了Passport(用于用户身份验证)、服务之间的消息传递、文件存储、用户个性设置的管理、日历管理和其他一些功能。Microsoft将在那些对.NET基本结构起至关重要作用的领域内提供一些块构建服务(building block services)。大量的合作伙伴和开发商将对这些块构建服务作重要的扩展。
  
    智能设备(smart device):
.NET利用软件使智能设备,诸如手提电脑、轻便电话、游戏操纵台等都能够在.NET世界中得以使用。
  
    一个智能设备应该:
  
    ●对用户要智能:能根据用户的
.NET身份、档案(profile)和有关数据简化用户的工作;另外要对用户的存在足够的智能,能根据你的在与不在对通知(notification)作出调整。
  
    ●对网络要智能:负责带宽的限制;支持应用程序的在线和线下两种使用模式;知道有哪些有效的服务。
  
    ●对信息要智能:能在任何地方、任何时间访问、分析和操作数据。
  
    ●对其他的设备要智能:能发现和报告其他智能设备、服务和Internet的存在;知道如何为其他设备提供服务;能够灵活方便地从PC上访问信息。
  
    ●对
软件和服务要智能:能根据表单的情况,最恰当地表现应用和数据;为终端用户提供合适的输入方法和连接;用XML、SOAP和UDDI来使用Web服务;对开发者来说,要具有可编程性和扩展性
  
    Microsoft的一些
软件使能够在智能设备上运行的,它们包括Windows XP、Windows Me、Windows CE、嵌入式Windows、.NET框架以及.NET Compact框架。
  
    
.NET体验(.NET experiences)
  
    终端用户是通过
.NET体验访问XML Web服务的,这和现有的独立应用程序有点类似,但在下列这些重要的方面是不同的:
  
    ●
.NET体验可使用于多种设备我们无需为可能使用的每一个设备编写一个不同XML Web服务和不同的.NET体验,.NET体验能够读取用户选取设备的特征,给出一个恰当界面。
  
    ●
.NET体验使用XML Web服务当.NET体验连入网络后就能有效地利用XML Web服务为用户带来额外的价值,以更好地解决问题。
  
    ●
.NET体验是以用户为中心的.NET体验的焦点在终端用户,使用基于身份验证的块构建服务来为用户验证、参数设定、通知机制和用户数据提供服务。因为用户数据是由块构建服务管理的,而不是应用程序本身,所以用户就能控制他们自己的数据,能保障它的正确性,并且可以在不同的程序和服务之间协调数据。
  
    Microsoft正在使最受欢迎的四个产品过渡到
.NET体验。Microsoft Office XP为用户提供.NET体验方面跨出了第一步。另外,MSN,包括MSN Explorer本地客户端的使用,正在创建一个基于消费者的.NET体验。Microsoft bCentral的小型商务入口(business portal)正努力为小型事务(比如商品目录管理)提供必要的XML Web服务,同时也使用一些重要的XML Web服务(比如eBay)。Visual Studio开发系统将为开发者们提供.NET体验,可以在这些开发工具中直接得到MSDN信息。
  
    
.NET的好处
  
    Microsoft
.NET为程序员、商业领导、IT部门以及消费者带来了很多好处。
  
    ●相对来说,程序员是比较缺乏的,雇用的费用也很高。然而Microsoft
.NET使编程工作变得更加容易,开发投资的回报率也趋最大化。开发者们可以创建能重用的XML Web服务,而不再是一个单一的程序;这些Web服务易于编程和调试,彼此之间相互独立,通过XML message通讯及合作。所以对某一个服务的修改不会影响到其他的服务。
  
    由于
XML Web服务可以被很多.NET体验共同使用,所以对一个服务模块的有效更新,也即更新了所有使用这个模块的.NET体验。任何编程语言都可以用来编写XML Web服务(如:C、C++、Visual Basic、COBOL、Perl、Python和Java等),所以你的程序员可以选择他们最熟悉的语言来编程,这大大提高了开发效率。更值得一体的是,他们并没有因使用了不同的语言而失去跨服务或跨组件的调试能力。
  
    ●Microsoft
.NET减少了程序员要写的代码量。一个XML Web服务能适用于所以的设备,不必再去为每一个设备编写一个不同的版本。另外,将显示特性与.NET体验分开以便以后加入新的接口技术,比如语音或手写识别,而不必去重写程序。
  
    ●Microsoft
.NET开创了全新的商业模型,它使得一个公司可以用多种方法来把自己的技术商品化。据个例子来说,一个通讯公司可以使用XML Web服务的方式提供语音信件和呼叫者ID的访问,让用户从一个即时消息程序、电子邮件或用户所选的其他信息编译器中访问到上述信息。技术提供商可以把他们现有的软件包转变为XML Web服务,并把这些服务出售给需要这些功能第三方,或是给.NET体验提供商,用以构建新的软件包。

●Microsoft
.NET允许IT部门使用其他提供商的XML Web服务,减少内部研发的开销,并能提高工作效率。
  
    ●Microsoft
.NET对"用户界面友好"作了重新定义。终端用户能够徜徉于一个智能化的、个性化的Internet,它能记住用户的个人设置,并在适当的时候,向用户使用的智能设备上发送适当的数据。

[1]

.NET如何改变计算
  
    Microsoft
.NET将从根本上改变我们的思考和使用电脑的方式。目前"服务器"和"桌面电脑"这两种概念占据了计算领域的统治地位。然而Microsoft .NET是一种分布式计算范例,它没有了传统上的服务器和桌面电脑的区别,取而代之的是,计算的处理被放在最合适的地方进行,可能是服务
  器,或是PC,也有可能是手提电脑以及其他智能设备。这就是智能计算。
  
    
.NET的计算模型对商务和终端用户都产生了重要影响,但方法不同。对终端用户来说,这个新计算模式更具个性化、综合程度更高,会给他们带来一种史无前例的新体验。对商务来说,这个模式改变了制造和销售软件的方法,使IT成为一个公司成功的重要贡献者,并建立起新的商务模型。
  
    对终端用户的改变
  
    这里有一个例子,说明了
.NET体验是如何对一个终端用户产生影响的。
  
    Bob,一个不安的商务旅行者,在芝加哥下了飞机,突然想起他竟忘了带上他的那部智能电话。这下完了,没了这电话他无法知道晚宴在哪里进行,无法知道原本打算在晚宴上见面的人的电话号码,更惨的是,他无法在这个关键的会议之前再看一下重要的文件。但不用急,他从机场的租了一部智能电话,插入了自己的智能卡。很快通过内置的Internet连接,各种相关的重要数据全部被下载了下来,现在他能访问他的所有信息,不光是日程安排和电话簿,还有所有通常用他的PC机能访问到的所以文件。
  
    不幸的是,他在离开机场时不小心在自动扶梯上绊了一跤,脚踝严重扭伤,这个月这已经是第二次了。无奈,他强忍疼痛要求电话接Roger医生的办公室,听电话的是接待员Mildred小姐。Bob通过电话确认了自己的身份,他授权于Mildred小姐,让她访问自己的所在位置和其他一些信息,以便使她可以在附近找一家整形外科诊所。Mildred能够知道哪家诊所正在营业,有多远,是否接受Bob的保险。Bob所要做的就是轻按电话上的按钮授权给她,Mildred在找到诊所后便会和医生约时间。
  
    与Mildred通话结束后,Bob用他的智能电话访问出租车服务,查找离他最近的出租车,并确认目的地。接下来Bob只需爬进车内,轻按电话的显示屏确认支付的费用即可。
  
    从用户角度来讲,
.NET提供的好处即超过了现在的独立的程序,也胜过了纯粹的Web站点。XML Web服务拥有传统的软件功能,如创建文档、计算数字、存储数据等。而且在下线后也能提供服务,比如呼叫出租车,这并不需要CPU的参与。
  
    从上面的例子我们可以看到,
XML Web服务使终端用户得到了更为个性化的、综合性的体验,同时便捷也是.NET给我们带来的一大好处。
  
    对企业的改变
  
    Bob那不走运的商务旅行结束了,他蹒跚地回到了家(虽然那个晚宴非常成功,但现在他不得不面对六个星期的身体治疗)。接下来,Bob要提交费用报告。他拿出了他的PDA,验明身份后,PDA列出了其信用卡上的支付纪录。他标出了与这次芝加哥之行有关的费用,至于那些止痛药和寄私人信件的费用,他标为个人开销。信用卡公司将为其生成必要的账单。
  
    因为Bob标记了一些个人费用,所以信用卡公司将根据他指定的方法为Bob生成一张个人帐单。在这个例子中,Bob使用的是直接从他银行账号中提钱的方法,但同时他也要一份药费开支报告的硬拷贝。根据他的选择,信用卡公司会Email发给他一个PDF文档,Bob只需将它打印出来即可。
  
    对于那些业务上的开支,信用卡公司会给Bob的公司发出一张电子帐单,它被送到公司的会计部门,由公司会计Chris来处理。电子帐单到达时,Chris会收到一封自动生成的Email,随后他登录会计系统打开这份帐单。他仔细检查每一笔费用,没问题后,他进行支付,这也就是授权将一笔金从公司的账户转移到信用卡公司的账户上。
  
    从企业角度讲,
.NET能够自动地处理很多任务,节约了员工的大量时间。当用XML将系统和XML Web服务连接起来后,数据交换变得非常方便,数据处理也变得轻而易举。在这个例子中,员工Bob和Chris分别只要单击一下"同意"和启动一个事务处理,无需花大量的时间去填写报销单或是往会计系统中手工录入数据,一切都变得非常之简单。
  
    对企业和企业终端用户来说,
.NET预示这些从XML Web服务衍生出来的应用程序有着很强的个性化和高度的整合性的特点,同时它们适用于各种智能设备,具有相当的灵活性。
  
    什么东西没有变
  
    尽管Microsoft
.NET给计算带来了一些翻天覆地的变化,但还有很多东西依然没有改变.
  
    ●终端用户将依然使用熟悉的界面,就像
.NET体验中的Microsoft Office一样。这可以减少再培训的开支,也意味着用户可以马上开始使用.NET软件
  
    ●
硬件上运行的还是象Windows、Unix、Windows CE和Palm OS一样的操作系统。实际上,.NET增加了软件的运行场所,但同时减少了开发的负担。由于XML Web服务只使用XML与设备通信,所以任何智能设备都可以享用XML Web服务。
  
    ●对程序员来说,他们依然可以使用他们原先熟悉的
编程语言。.NET平台借助于.NET框架的公共语言运行时间库(CLR)使得用不同语言开发的XML Web服务之间也可以相互操作。有没有.NET体验问题不大,你依旧可以用Visual Basic、Java、甚至是COBOL创建XML Web服务。这种对编程语言的中立性意味着不用为了进入.NET世界而抛弃已有的投资。
  
    ●原先系统无需被替换。一部分的Microsoft
.NET产品就是为了能方便地将现有的系统整合到新,的XML Web服务和.NET体验中去而设计的。Host Integration Server就是个例子,它简化了对主机的访问。再比如就是BizTalk Server,它管理的商务流程(business process orchestration)包括了对现有系统和数据格式的支持,并会执行一些必要的转换,将数据转成XML
  
     所以这种下一代的分布式计算是建立在目前这一代基础上的。Microsoft
.NET不是我们所想象的那样,对现在的应用软件作大规模的替换,而是一个自然的进化过程,在原先的技术孤岛之间建立了协作关系,协同工作能力逐渐加强,我们也将从中受益无穷。
  
    总结
  
    Microsoft
.NET是Microsoft的XML Web服务的平台。这是下一代的Internet计算模型,各个XML Web服务之间彼此是松耦合的,通过XML进行通讯,协同完成某一特定的任务。Microsoft .NET战略提供了一个用以建立新.NET体验的软件平台、一个编程模型、用以建立和整合XML Web服务的工具以及一套可编程的Web接口。
  
    现在我们正处于向
.NET转变的过程中。Microsoft已经宣布了.NET框架的第一个部分--.NET平台、Visual Studio.NET和一些块构建服务以及最初的.NET体验。Microsoft在今年和明年中将会提供更多的工具和服务。
posted @ 2006-09-28 11:03 Bourne 阅读(265) | 评论 (0)编辑 收藏
原型:extern void *memcpy(void *dest, void *src, unsigned int count);

用法:#include <string.h>

功能:由src所指内存区域复制count个字节到dest所指内存区域。

说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针。

举例:

// memcpy.c

#include <syslib.h>
#include <string.h>

main()
{
char *s="Golden Global View";
char d[20];

clrscr();

memcpy(d,s,strlen(s));
d[strlen(s)]=0;
printf("%s",d);

getchar();
return 0;
}

原型:extern char *strchr(char *s,char c);

用法:#include <string.h>

功能:查找字符串s中首次出现字符c的位置

说明:返回首次出现c的位置的指针,如果s中不存在c则返回NULL。

举例:


// strchr.c

#include <syslib.h>
#include <string.h>

main()
{
char *s="Golden Global View";
char *p;

clrscr();

strchr(s,'V');
if(p)
printf("%s",p);
else
printf("Not Found!");

getchar();
return 0;
}

1 复制

 

char* strcpy (char *s1, const char *s2);
将字符串s2复制到s1指定的地址

 

char* strncpy (char *s1, const char *s2, size_t len);
void*  memcpy (void *s1, const void *s2, size_t len);
s2的前len个字符(字节)复制到s1中指定的地址, 不加'\0'

 

void* memmove (void *s1, const void *s2, size_t len);
当源单元和目的单元缓冲区交迭时使用

 

size_t strxfrm (char *s1, const char *s1, size_t len);
根据程序当前的区域选项, s2的前len个字符(字节)复制到s1中指定的地址, 不加'\0'

 


2
连接

 

char* strcat (char *s1, const char *s2);
将字符串s2连接到s1尾部

 

char* strncat (char *s1, const char *s2, size_t len);
将字符串s2的前len个字符连接到s1尾部, 不加'\0'

 


3
比较

 

int strcmp (const char *s1, const char *s2);
比较字符串s1s2

 

int strncmp (const char *s1, const char *s2, size_t len);
int  memcmp (const void *s1, const void *s2, size_t len);
s1s2的前len个字符(字节)作比较

 

int strcoll (const char *s1, const char *s2);
根据程序当前的区域选项中的LC_COLLATE, 比较字符串s1s2

 


4
查找

 

char* strchr (const char *s, int ch);
void* memchr (const void *s, int ch, size_t len);

s中查找给定字符(字节值)ch第一次出现的位置

 

char* strrchr (const char *s, int ch);
在串s中查找给定字符ch最后一次出现的位置, r表示从串尾开始

 

char* strstr (const char *s1, const char *s2);
在串s1中查找指定字符串s2第一次出现的位置

 

size_t strspn (const char *s1, const char *s2);
返回s1中第一个在s2中不存在的字符的索引(find_first_not_of)

 

size_t strcspn (const char *s1, const char *s2);
返回s1中第一个也在s2中存在的字符的索引(find_first_of)

 

char* strpbrk (const char *s1, const char *s2);
strcspn类似, 区别是返回指针而不是索引

 

char* strtok (char *s1, const char *s2);
从串s1中分离出由串s2中指定的分界符分隔开的记号
(token)
第一次调用时s1为需分割的字串, 此后每次调用都将s1置为
NULL,
每次调用strtok返回一个记号, 直到返回NULL为止

 


5
其他

 

size_t strlen (const char *s);
求字符串s的长度

 

void* memset (void *s, int val, size_t len);
将从s开始的len个字节置为val

 

char* strerror (int errno);
返回指向错误信息字符串的指针

 

source: C & C++ Code Capsules
posted @ 2006-07-28 10:35 Bourne 阅读(257) | 评论 (0)编辑 收藏

#i nclude <stdio.h>
#i nclude <stdlib.h>
#i nclude <string.h>
#i nclude <time.h>

//获得prefix数组
int* GetPrefixValue(char* strPattern, int iPatternLen)
{
    int i, j; /* i runs through the string, j counts the hits*/
    int* prefix = (int*)malloc(iPatternLen*sizeof(int));
   
    i = 1; j = 0;
    prefix[0] = 0;
   
    while(i<iPatternLen)
    {
        if(strPattern[i] == strPattern[j])
        {
            prefix[i] = ++j;
        }
        else
        {
            j = 0;
            prefix[i] = j;
        }
       
        i++;
    }
   
    return prefix;          
}   

//返回target串在pattern串中第一次匹配的index
int KMPStringMatch(char* strPattern, int iPatternLen, char* strTarget, int iTargetLen, int* prefix)
{
    int i = 0;
    int j = 0;
   
    while(i<iPatternLen && j<iTargetLen)
    {
        if(j==0 || strPattern[i]==strTarget[j])
        {
            i++;  j++;
        }
        else
        {
            j = prefix[j];
        }
    }            
   
    free(prefix);
    
    if(j==iTargetLen)
    {
        return i-j;
    }
    else
    {
        return -1;
    }        
}        

int KMP(char* strPattern, char* strTarget)
{
    int* prefix = GetPrefixValue(strPattern, strlen(strPattern));
    int index = KMPStringMatch(strPattern, strlen(strPattern), strTarget, strlen(strTarget), prefix);
    return index;
}

//在文本文件中查找target串出现的行数
int SearchInTxtFile(char* fileName, char* strTarget)
{
    FILE* hFile = fopen(fileName, "r");
   
    char str[1024];
    int count = 0;
   
   
    while(fgets(str, 1024, hFile)) 
    {
        if(KMP(str, strTarget)!=-1)
        {
            count++;
        }   
    }     
   
    fclose(hFile);
    hFile=NULL;
   
    return count;
}    
            
int main()
{
    char ch;
    char str1[] = "abcabcabctasksb,abTo";
    char str2[] = "abc";
   
    double t=clock();
    printf("%d\n", KMP(str1,str2));
    printf("耗时:%f毫秒!\n", (clock()-t));
   
    t=clock();
    printf("find %d \n", SearchInTxtFile("c:\\txt.txt", "NULL"));
    printf("耗时:%f毫秒!\n", (clock()-t));
    scanf("%c", &ch);

    return 0;

posted @ 2006-07-05 23:48 Bourne 阅读(4753) | 评论 (4)编辑 收藏
摘要:
本文从介绍基础概念入手,探讨了在C/C++中对日期和时间操作所用到的数据结构和函数,并对计时、时间的获取、时间的计算和显示格式等方面进行了阐述。本文还通过大量的实例向你展示了time.h头文件中声明的各种函数和数据结构的详细使用方法。

关键字:UTC(世界标准时间),Calendar Time(日历时间),epoch(时间点),clock tick(时钟计时单元)

1.概念
在C/C++中,对字符串的操作有很多值得注意的问题,同样,C/C++对时间的操作也有许多值得大家注意的地方。最近,在技术群中有很多网友也多次问到过C++语言中对时间的操作、获取和显示等等的问题。下面,在这篇文章中,笔者将主要介绍在C/C++中时间和日期的使用方法.

通过学习许多C/C++库,你可以有很多操作、使用时间的方法。但在这之前你需要了解一些“时间”和“日期”的概念,主要有以下几个:

Coordinated Universal Time(UTC):协调世界时,又称为世界标准时间,也就是大家所熟知的格林威治标准时间(Greenwich Mean Time,GMT)。比如,中国内地的时间与UTC的时差为+8,也就是UTC+8。美国是UTC-5。

Calendar Time:日历时间,是用“从一个标准时间点到此时的时间经过的秒数”来表示的时间。这个标准时间点对不同的编译器来说会有所不同,但对一个编译系统来说,这个标准时间点是不变的,该编译系统中的时间对应的日历时间都通过该标准时间点来衡量,所以可以说日历时间是“相对时间”,但是无论你在哪一个时区,在同一时刻对同一个标准时间点来说,日历时间都是一样的。

epoch:时间点。时间点在标准C/C++中是一个整数,它用此时的时间和标准时间点相差的秒数(即日历时间)来表示。

clock tick:时钟计时单元(而不把它叫做时钟滴答次数),一个时钟计时单元的时间长短是由CPU控制的。一个clock tick不是CPU的一个时钟周期,而是C/C++的一个基本计时单位。

我们可以使用ANSI标准库中的time.h头文件。这个头文件中定义的时间和日期所使用的方法,无论是在结构定义,还是命名,都具有明显的C语言风格。下面,我将说明在C/C++中怎样使用日期的时间功能。

2. 计时

C/C++中的计时函数是clock(),而与其相关的数据类型是clock_t。在MSDN中,查得对clock函数定义如下:

clock_t clock( void );

这个函数返回从“开启这个程序进程”到“程序中调用clock()函数”时之间的CPU时钟计时单元(clock tick)数,在MSDN中称之为挂钟时间(wal-clock)。其中clock_t是用来保存时间的数据类型,在time.h文件中,我们可以找到对它的定义:

#ifndef _CLOCK_T_DEFINED
typedef long clock_t;
#define _CLOCK_T_DEFINED
#endif

很明显,clock_t是一个长整形数。在time.h文件中,还定义了一个常量CLOCKS_PER_SEC,它用来表示一秒钟会有多少个时钟计时单元,其定义如下:

#define CLOCKS_PER_SEC ((clock_t)1000)

可以看到每过千分之一秒(1毫秒),调用clock()函数返回的值就加1。下面举个例子,你可以使用公式clock()/CLOCKS_PER_SEC来计算一个进程自身的运行时间:

void elapsed_time()
{
printf("Elapsed time:%u secs.\n",clock()/CLOCKS_PER_SEC);
}

当然,你也可以用clock函数来计算你的机器运行一个循环或者处理其它事件到底花了多少时间:

#include “stdio.h”
#include “stdlib.h”
#include “time.h”

int main( void )
{
   long    i = 10000000L;
   clock_t start, finish;
   double  duration;
   /* 测量一个事件持续的时间*/
   printf( "Time to do %ld empty loops is ", i );
   start = clock();
   while( i-- )      ;
   finish = clock();
   duration = (double)(finish - start) / CLOCKS_PER_SEC;
   printf( "%f seconds\n", duration );
   system("pause");
}

在笔者的机器上,运行结果如下:

Time to do 10000000 empty loops is 0.03000 seconds

上面我们看到时钟计时单元的长度为1毫秒,那么计时的精度也为1毫秒,那么我们可不可以通过改变CLOCKS_PER_SEC的定义,通过把它定义的大一些,从而使计时精度更高呢?通过尝试,你会发现这样是不行的。在标准C/C++中,最小的计时单位是一毫秒。

3.与日期和时间相关的数据结构

在标准C/C++中,我们可通过tm结构来获得日期和时间,tm结构在time.h中的定义如下:

#ifndef _TM_DEFINED
struct tm {
        int tm_sec;     /* 秒 – 取值区间为[0,59] */
        int tm_min;     /* 分 - 取值区间为[0,59] */
        int tm_hour;    /* 时 - 取值区间为[0,23] */
        int tm_mday;    /* 一个月中的日期 - 取值区间为[1,31] */
        int tm_mon;     /* 月份(从一月开始,0代表一月) - 取值区间为[0,11] */
        int tm_year;    /* 年份,其值等于实际年份减去1900 */
        int tm_wday;    /* 星期 – 取值区间为[0,6],其中0代表星期天,1代表星期一,以此类推 */
        int tm_yday;    /* 从每年的1月1日开始的天数 – 取值区间为[0,365],其中0代表1月1日,1代表1月2日,以此类推 */
        int tm_isdst;   /* 夏令时标识符,实行夏令时的时候,tm_isdst为正。不实行夏令时的进候,tm_isdst为0;不了解情况时,tm_isdst()为负。*/
        };
#define _TM_DEFINED
#endif

ANSI C标准称使用tm结构的这种时间表示为分解时间(broken-down time)。

而日历时间(Calendar Time)是通过time_t数据类型来表示的,用time_t表示的时间(日历时间)是从一个时间点(例如:1970年1月1日0时0分0秒)到此时的秒数。在time.h中,我们也可以看到time_t是一个长整型数:

#ifndef _TIME_T_DEFINED
typedef long time_t;         /* 时间值 */
#define _TIME_T_DEFINED      /* 避免重复定义 time_t */
#endif

大家可能会产生疑问:既然time_t实际上是长整型,到未来的某一天,从一个时间点(一般是1970年1月1日0时0分0秒)到那时的秒数(即日历时间)超出了长整形所能表示的数的范围怎么办?对time_t数据类型的值来说,它所表示的时间不能晚于2038年1月18日19时14分07秒。为了能够表示更久远的时间,一些编译器厂商引入了64位甚至更长的整形数来保存日历时间。比如微软在Visual C++中采用了__time64_t数据类型来保存日历时间,并通过_time64()函数来获得日历时间(而不是通过使用32位字的time()函数),这样就可以通过该数据类型保存3001年1月1日0时0分0秒(不包括该时间点)之前的时间。

在time.h头文件中,我们还可以看到一些函数,它们都是以time_t为参数类型或返回值类型的函数:

double difftime(time_t time1, time_t time0);
time_t mktime(struct tm * timeptr);
time_t time(time_t * timer);
char * asctime(const struct tm * timeptr);
char * ctime(const time_t *timer);

此外,time.h还提供了两种不同的函数将日历时间(一个用time_t表示的整数)转换为我们平时看到的把年月日时分秒分开显示的时间格式tm:

struct tm * gmtime(const time_t *timer);                                          
struct tm * localtime(const time_t * timer);

通过查阅MSDN,我们可以知道Microsoft C/C++ 7.0中时间点的值(time_t对象的值)是从1899年12月31日0时0分0秒到该时间点所经过的秒数,而其它各种版本的Microsoft C/C++和所有不同版本的Visual C++都是计算的从1970年1月1日0时0分0秒到该时间点所经过的秒数。

4.与日期和时间相关的函数及应用
在本节,我将向大家展示怎样利用time.h中声明的函数对时间进行操作。这些操作包括取当前时间、计算时间间隔、以不同的形式显示时间等内容。

4.1 获得日历时间

我们可以通过time()函数来获得日历时间(Calendar Time),其原型为:

time_t time(time_t * timer);

如果你已经声明了参数timer,你可以从参数timer返回现在的日历时间,同时也可以通过返回值返回现在的日历时间,即从一个时间点(例如:1970年1月1日0时0分0秒)到现在此时的秒数。如果参数为空(NUL),函数将只通过返回值返回现在的日历时间,比如下面这个例子用来显示当前的日历时间:

#include "time.h"
#include "stdio.h"
int main(void)
{
struct tm *ptr;
time_t lt;
lt =time(NUL);
printf("The Calendar Time now is %d\n",lt);
return 0;
}

运行的结果与当时的时间有关,我当时运行的结果是:

The Calendar Time now is 1122707619

其中1122707619就是我运行程序时的日历时间。即从1970年1月1日0时0分0秒到此时的秒数。

4.2 获得日期和时间

这里说的日期和时间就是我们平时所说的年、月、日、时、分、秒等信息。从第2节我们已经知道这些信息都保存在一个名为tm的结构体中,那么如何将一个日历时间保存为一个tm结构的对象呢?

其中可以使用的函数是gmtime()和localtime(),这两个函数的原型为:

struct tm * gmtime(const time_t *timer);                                          
struct tm * localtime(const time_t * timer);

其中gmtime()函数是将日历时间转化为世界标准时间(即格林尼治时间),并返回一个tm结构体来保存这个时间,而localtime()函数是将日历时间转化为本地时间。比如现在用gmtime()函数获得的世界标准时间是2005年7月30日7点18分20秒,那么我用localtime()函数在中国地区获得的本地时间会比世界标准时间晚8个小时,即2005年7月30日15点18分20秒。下面是个例子:

#include "time.h"
#include "stdio.h"
int main(void)
{
struct tm *local;
time_t t;
t=time(NUL);
local=localtime(&t);
printf("Local hour is: %d\n",local->tm_hour);
local=gmtime(&t);
printf("UTC hour is: %d\n",local->tm_hour);
return 0;
}

运行结果是:

Local hour is: 15
UTC hour is: 7

4.3 固定的时间格式

我们可以通过asctime()函数和ctime()函数将时间以固定的格式显示出来,两者的返回值都是char*型的字符串。返回的时间格式为:

星期几 月份 日期 时:分:秒 年\n\0
例如:Wed Jan 02 02:03:55 1980\n\0

其中\n是一个换行符,\0是一个空字符,表示字符串结束。下面是两个函数的原型:

char * asctime(const struct tm * timeptr);
char * ctime(const time_t *timer);

其中asctime()函数是通过tm结构来生成具有固定格式的保存时间信息的字符串,而ctime()是通过日历时间来生成时间字符串。这样的话,asctime()函数只是把tm结构对象中的各个域填到时间字符串的相应位置就行了,而ctime()函数需要先参照本地的时间设置,把日历时间转化为本地时间,然后再生成格式化后的字符串。在下面,如果t是一个非空的time_t变量的话,那么:

printf(ctime(&t));

等价于:

struct tm *ptr;
ptr=localtime(&t);
printf(asctime(ptr));

那么,下面这个程序的两条printf语句输出的结果就是不同的了(除非你将本地时区设为世界标准时间所在的时区):

#include "time.h"
#include "stdio.h"
int main(void)
{
struct tm *ptr;
time_t lt;
lt =time(NUL);
ptr=gmtime(&lt);
printf(asctime(ptr));
printf(ctime(&lt));
return 0;
}

运行结果:

Sat Jul 30 08:43:03 2005
Sat Jul 30 16:43:03 2005

4.4 自定义时间格式

我们可以使用strftime()函数将时间格式化为我们想要的格式。它的原型如下:

size_t strftime(
   char *strDest,
   size_t maxsize,
   const char *format,
   const struct tm *timeptr
);

我们可以根据format指向字符串中格式命令把timeptr中保存的时间信息放在strDest指向的字符串中,最多向strDest中存放maxsize个字符。该函数返回向strDest指向的字符串中放置的字符数。

函数strftime()的操作有些类似于sprintf():识别以百分号(%)开始的格式命令集合,格式化输出结果放在一个字符串中。格式化命令说明串strDest中各种日期和时间信息的确切表示方法。格式串中的其他字符原样放进串中。格式命令列在下面,它们是区分大小写的。

%a 星期几的简写
%A 星期几的全称
%b 月分的简写
%B 月份的全称
%c 标准的日期的时间串
%C 年份的后两位数字
%d 十进制表示的每月的第几天
%D 月/天/年
%e 在两字符域中,十进制表示的每月的第几天
%F 年-月-日
%g 年份的后两位数字,使用基于周的年
%G 年分,使用基于周的年
%h 简写的月份名
%H 24小时制的小时
%I 12小时制的小时
%j 十进制表示的每年的第几天
%m 十进制表示的月份
%M 十时制表示的分钟数
%n 新行符
%p 本地的AM或PM的等价显示
%r 12小时的时间
%R 显示小时和分钟:hh:mm
%S 十进制的秒数
%t 水平制表符
%T 显示时分秒:hh:mm:ss
%u 每周的第几天,星期一为第一天 (值从0到6,星期一为0)
%U 第年的第几周,把星期日做为第一天(值从0到53)
%V 每年的第几周,使用基于周的年
%w 十进制表示的星期几(值从0到6,星期天为0)
%W 每年的第几周,把星期一做为第一天(值从0到53)
%x 标准的日期串
%X 标准的时间串
%y 不带世纪的十进制年份(值从0到99)
%Y 带世纪部分的十进制年份
%z,%Z 时区名称,如果不能得到时区名称则返回空字符。
%% 百分号

如果想显示现在是几点了,并以12小时制显示,就象下面这段程序:

#include “time.h”
#include “stdio.h”
int main(void)
{
struct tm *ptr;
time_t lt;
char str[80];
lt=time(NUL);
ptr=localtime(&lt);
strftime(str,100,"It is now %I %p",ptr);
printf(str);
return 0;
}

其运行结果为:
It is now 4PM

而下面的程序则显示当前的完整日期:

#include <stdio.h>
#include <time.h>

void main( void )
{
        struct tm *newtime;
        char tmpbuf[128];
        time_t lt1;
        time( &lt1 );
        newtime=localtime(&lt1);
        strftime( tmpbuf, 128, "Today is %A, day %d of %B in the year %Y.\n", newtime);
        printf(tmpbuf);
}

运行结果:

Today is Saturday, day 30 of July in the year 2005.

4.5 计算持续时间的长度

有时候在实际应用中要计算一个事件持续的时间长度,比如计算打字速度。在第1节计时部分中,我已经用clock函数举了一个例子。Clock()函数可以精确到毫秒级。同时,我们也可以使用difftime()函数,但它只能精确到秒。该函数的定义如下:

double difftime(time_t time1, time_t time0);

虽然该函数返回的以秒计算的时间间隔是double类型的,但这并不说明该时间具有同double一样的精确度,这是由它的参数觉得的(time_t是以秒为单位计算的)。比如下面一段程序:

#include "time.h"
#include "stdio.h"
#include "stdlib.h"
int main(void)
{
time_t start,end;
start = time(NUL);
system("pause");
end = time(NUL);
printf("The pause used %f seconds.\n",difftime(end,start));//<-
system("pause");
return 0;
}

运行结果为:
请按任意键继续. . .
The pause used 2.000000 seconds.
请按任意键继续. . .

可以想像,暂停的时间并不那么巧是整整2秒钟。其实,你将上面程序的带有“//<-”注释的一行用下面的一行代码替换:

printf("The pause used %f seconds.\n",end-start);

其运行结果是一样的。

4.6 分解时间转化为日历时间

这里说的分解时间就是以年、月、日、时、分、秒等分量保存的时间结构,在C/C++中是tm结构。我们可以使用mktime()函数将用tm结构表示的时间转化为日历时间。其函数原型如下:

time_t mktime(struct tm * timeptr);

其返回值就是转化后的日历时间。这样我们就可以先制定一个分解时间,然后对这个时间进行操作了,下面的例子可以计算出1997年7月1日是星期几:

#include "time.h"
#include "stdio.h"
#include "stdlib.h"
int main(void)
{
struct tm t;
time_t t_of_day;
t.tm_year=1997-1900;
t.tm_mon=6;
t.tm_mday=1;
t.tm_hour=0;
t.tm_min=0;
t.tm_sec=1;
t.tm_isdst=0;
t_of_day=mktime(&t);
printf(ctime(&t_of_day));
return 0;
}

运行结果:

Tue Jul 01 00:00:01 1997

现在注意了,有了mktime()函数,是不是我们可以操作现在之前的任何时间呢?你可以通过这种办法算出1945年8月15号是星期几吗?答案是否定的。因为这个时间在1970年1月1日之前,所以在大多数编译器中,这样的程序虽然可以编译通过,但运行时会异常终止。

5.总结

本文介绍了标准C/C++中的有关日期和时间的概念,并通过各种实例讲述了这些函数和数据结构的使用方法。笔者认为,和时间相关的一些概念是相当重要的,理解这些概念是理解各种时间格式的转换的基础,更是应用这些函数和数据结构的基础。

参考文献

[1] 标准C++程序设计教程,电子工业出版社,2003。
[2] MSDN Library, Microsoft Corporation,2003。
posted @ 2006-07-05 12:12 Bourne 阅读(3817) | 评论 (1)编辑 收藏

上网搜东西的时候搜到了这个,觉得蛮使用的,放在这里以备后用!

--  LINK2001
学习VC++时经常会遇到链接错误LNK2001,该错误非常讨厌,因为对于
编程者来说,最好改的错误莫过于编译错误,而一般说来发生连接错误时,
编译都已通过。产生连接错误的原因非常多,尤其LNK2001错误,常常使人不
明其所以然。如果不深入地学习和理解VC++,要想改正连接错误LNK2001非
常困难。
  初学者在学习VC++的过程中,遇到的LNK2001错误的错误消息主要为:
  unresolved external symbol “symbol”(不确定的外部“符号”)。
  如果连接程序不能在所有的库和目标文件内找到所引用的函数、变量或
标签,将产生此错误消息。一般来说,发生错误的原因有两个:一是所引用
的函数、变量不存在、拼写不正确或者使用错误;其次可能使用了不同版本
的连接库。
  以下是可能产生LNK2001错误的原因:
  一.由于编码错误导致的LNK2001。
  1.不相匹配的程序代码或模块定义(.DEF)文件能导致LNK2001。例如,
如果在C++ 源文件内声明了一变量“var1”,却试图在另一文件内以变量
“VAR1”访问该变量,将发生该错误。
  2.如果使用的内联函数是在.CPP文件内定义的,而不是在头文件内定
义将导致LNK2001错误。
  3.调用函数时如果所用的参数类型同函数声明时的类型不符将会产生
LNK2001。
  4.试图从基类的构造函数或析构函数中调用虚拟函数时将会导致LNK2001。
  5.要注意函数和变量的可公用性,只有全局变量、函数是可公用的。
  静态函数和静态变量具有相同的使用范围限制。当试图从文件外部访问
任何没有在该文件内声明的静态变量时将导致编译错误或LNK2001。
  函数内声明的变量(局部变量) 只能在该函数的范围内使用。
  C++ 的全局常量只有静态连接性能。这不同于C,如果试图在C++的
多个文件内使用全局变量也会产生LNK2001错误。一种解决的方法是需要时在
头文件中加入该常量的初始化代码,并在.CPP文件中包含该头文件;另一种
方法是使用时给该变量赋以常数。
  二.由于编译和链接的设置而造成的LNK2001
  1.如果编译时使用的是/NOD(/NODEFAULTLIB)选项,程序所需要的运行
库和MFC库在连接时由编译器写入目标文件模块, 但除非在文件中明确包含
这些库名,否则这些库不会被链接进工程文件。在这种情况下使用/NOD将导
致错误LNK2001。
  2.如果没有为wWinMainCRTStartup设定程序入口,在使用Unicode和MFC
时将得到“unresolved external on _WinMain@16”的LNK2001错误信息。
  3.使用/MD选项编译时,既然所有的运行库都被保留在动态链接库之内,
源文件中对“func”的引用,在目标文件里即对“__imp__func” 的引用。
如果试图使用静态库LIBC.LIB或LIBCMT.LIB进行连接,将在__imp__func上发
生LNK2001;如果不使用/MD选项编译,在使用MSVCxx.LIB连接时也会发生LNK2001。
  4.使用/ML选项编译时,如用LIBCMT.LIB链接会在_errno上发生LNK2001。
  5.当编译调试版的应用程序时,如果采用发行版模态库进行连接也会产
生LNK2001;同样,使用调试版模态库连接发行版应用程序时也会产生相同的
问题。
  6.不同版本的库和编译器的混合使用也能产生问题,因为新版的库里可
能包含早先的版本没有的符号和说明。
  7.在不同的模块使用内联和非内联的编译选项能够导致LNK2001。如果
创建C++库时打开了函数内联(/Ob1或/Ob2),但是在描述该函数的相应头
文件里却关闭了函数内联(没有inline关键字),这时将得到该错误信息。
为避免该问题的发生,应该在相应的头文件中用inline关键字标志内联函数。
  8.不正确的/SUBSYSTEM或/ENTRY设置也能导致LNK2001。
  其实,产生LNK2001的原因还有很多,以上的原因只是一部分而已,对初
学者来说这些就够理解一阵子了。但是,分析错误原因的目的是为了避免错
误的发生。LNK2001错误虽然比较困难,但是只要注意到了上述问题,还是能
够避免和予以解决的。

posted @ 2006-06-20 16:36 Bourne 阅读(2449) | 评论 (2)编辑 收藏

还是我做的那个链表的模板类
我分别写了头文件Chain.h,源文件Chain.cpp,并且在main.cpp中进行测试。
但是在连接的时候出现了问题,不知道什么原因,希望能够有高手指点一下,非常感谢!

其中Chain.h声明了类的数据成员和成员函数,具体内容如下:

#ifndef _CHAIN_H
#define _CHAIN_H

#include <iostream>
using namespace std;

template<class T>
class Chain;


template<class T>
class ChainNode
{
 friend class Chain<T>;
 friend ostream& operator<<(ostream& out, const Chain<T>& x);
private:
 T data;
 ChainNode<T> *link;
};

template<class T>
class Chain{

public:
 Chain(int p) {first = 0;};
 ~Chain();
 bool IsEmpty() const {return first == 0;}
 int Length() const;
 bool Find(int k, T& x) const;
 int Search(const T& x) const;
 //Chain<T>& Delete(int k, T& x);
 Chain<T>& Insert(int k, const T& x);
 void Output(ostream& out = cout) const;
private:
 ChainNode<T> *first; // 指向第一个节点的指针
};

template<class T>
ostream& operator<<(ostream& out, const Chain<T>& x);

#endif  // _CHAIN

  在Chain.cpp中对Chain.h中声明的函数进行了定义,具体内容如下:

#include "Chain.h"
#include <iostream>
using namespace std;

template<class T>
Chain<T>::~Chain()
{
 //链表的析构函数,用于删除链表中的所有节点
 ChainNode<T> *ptr = first;
 while (ptr)
 {
  first = ptr->link;
  delete ptr;
  ptr = first;
 }
}

template<class T>
int Chain<T>::Length() const
{
 //返回链表中的元素总数
 int count = 0;
 ChainNode<T> *ptr = first;
 while (ptr)
 {
  ++count;
  ptr = ptr->link;
 }
 return count;
}

template<class T>
bool Chain<T>::Find(int k, T& x) const
{
 //寻找链表中的第k个元素,并将其传送至x
 //如果不存在第k个元素,则返回false,否则返回true
 if (k < 1)
 {
  return false;
 }
 int count = k;
 ChainNode<T> *ptr = first;
 while (count && ptr)
 {
  --count;
  ptr = ptr->link
 }
 if (!ptr)
 {
  return false;
 }
 x = ptr->data;
 return true;
}

template<class T>
int Chain<T>::Search(const T& x) const
{
 //寻找x,如果发现x,则返回x的地址
 //如果x不在链表中,则返回0
 int count = 1;
 ChainNode<T> *ptr = first;
 while(ptr && (ptr->data!=x))
 {
  ++count;
  ptr = ptr->link;
 }
 if (ptr)
 {
  return count;
 }
 else
 {
  return 0;
 }
}

template<class T>
void Chain<T>::Output(ostream& out = cout) const
{
 ChainNode<T> *ptr = first;
 while (ptr)
 {
  out<<ptr->data<<"  ";
  ptr = ptr->link;
 }
}

//重载<<运算符
template<class T>
ostream& operator<<(ostream& out, const Chain<T>& x)
{
 x.Output(out);
 return out;
}

template<class T>
Chain<T>& Chain<T>::Insert(int k, const T& x)
{
 ChainNode<T> *ptr = first;
 int count = 0;
 while (ptr && count < k)
 {
  ++count;
  ptr = ptr->link;
 }
 if (!ptr)  //如果没到第k个节点
 {
 
 }
 else
 {
  //要插入的新节点
  ChainNode<T>* cn = new ChainNode<T>;
  cn->data = x;
  cn->link = 0;
  if (ptr->link==0)  //到达了链表的结尾
  {
   ptr->link = cn;
  }
  else  //没到达结尾
  { 
   cn->link = ptr->link;
   ptr->link = cn;
  }
 }
 return *this
}

 在main.cpp中进行测试,测试的内容很少,但是刚开始就进行不了了。main.cpp内容如下:

#include "Chain.h"
using namespace std;

int main()
{
 Chain<int> c;
 cout<<c.Length();
 return 0;
}

编译的时候没有问题,但是在连接的时候就出现了问题,报错如下:
--------------------Configuration: Chain - Win32 Debug--------------------
Linking...
main.obj : error LNK2001: unresolved external symbol "public: __thiscall Chain<int>::~Chain<int>(void)" (
??1?$Chain@H@@QAE@XZ)
main.obj : error LNK2001: unresolved external symbol "public: int __thiscall Chain<int>::Length(void)const " (
?Length@?$Chain@H@@QBEHXZ)
Debug/Chain.exe : fatal error LNK1120: 2 unresolved externals
Error executing link.exe.

Chain.exe - 3 error(s), 0 warning(s)
但是从报错信息来看,应该是在main.cpp中没有找到所用到的函数 ~Chain<int>(void)和Length()的定义,在main.cpp中一共用到了三个函数,构造函数Chain(),但是构造函数是在Chain.h中定义的,所以编译器找到了其定义,但是另外两个函数是在Chain.cpp中定义的,而且目前报错没有找到,但是如果在main.cpp中引入#include "Chain.cpp"时,编译和连接就没有问题,这就证实了原来的估计是没有错的。我实在是不知道问题出现在哪里,所以希望哪位高手看出问题来的话,请告诉我,多谢了!

posted @ 2006-06-19 16:56 Bourne 阅读(819) | 评论 (4)编辑 收藏

以前写代码的时候就遇到VC++对友元支持得不太好的问题,同时也看过侯捷老师对gnu c++, VC++, BCB 三种编译器的比较,其中VC++对模板友元的支持就不是很好。
今天晚上写了一个比较简单的链表的模板类,其中头文件Chain.h原来的代码如下:

#include <iostream>
using namespace std;

#ifndef _CHAIN
#define _CHAIN

template<class T>
class ChainNode
{
 friend class Chain<T>;
private:
 T data;
 ChainNode<T> *link;
};

template<class T>
class Chain{
public:
 Chain()
 {
  first = 0;
 };
 ~Chain();
 bool IsEmpty() const {return first == 0;}
 int Length() const;
 bool Find(int k, T& x) const;
 int Search(const T& x) const;
 //Chain<T>& Delete(int k, T& x);
 Chain<T>& Insert(int k, const T& x);
 void Output(ostream& out = cout) const;
private:
 ChainNode<T> *first; // 指向第一个节点的指针
};

#endif  // _CHAIN

结果报错:
--------------------Configuration: Chain - Win32 Debug--------------------
Compiling...
Chain.cpp
g:\work plan\c++ code practice\chain\chain.h(17) : error C2059: syntax error : '<'
        g:\work plan\c++ code practice\chain\chain.h(21) : see reference to class template instantiation 'ChainNode<T>' being compiled
g:\work plan\c++ code practice\chain\chain.h(17) : error C2238: unexpected token(s) preceding ';'
        g:\work plan\c++ code practice\chain\chain.h(21) : see reference to class template instantiation 'ChainNode<T>' being compiled
g:\work plan\c++ code practice\chain\chain.h(40) : error C2989: 'Chain' : template class has already been defined as a non-template class
        g:\work plan\c++ code practice\chain\chain.h(17) : see declaration of 'Chain'
g:\work plan\c++ code practice\chain\chain.cpp(6) : error C2059: syntax error : '<'
g:\work plan\c++ code practice\chain\chain.cpp(6) : error C2588: '::~Chain' : illegal global destructor
g:\work plan\c++ code practice\chain\chain.cpp(6) : fatal error C1903: unable to recover from previous error(s); stopping compilation
Error executing cl.exe.

Chain.obj - 6 error(s), 0 warning(s)

感觉从代码来看应该是没有问题的,如果哪个高手看出问题来了请一定告诉我啊,如果知道编译不通过的原因也请一定要告诉我啊。没办法,最后采用解决的办法就是修改ChainNode的定义了,定义为结构体:)
template<class T>
struct ChainNode
{
  T data;
  ChainNode<T> *link;
};

反正结构体中的数据成员都是public的,至于访问限制的实现就依靠迭代器来实现了,g++的STL中的树结点不也是结构体吗?:)

posted @ 2006-06-17 23:39 Bourne 阅读(2615) | 评论 (4)编辑 收藏
仅列出标题