论坛: 编程破解 标题: 标题:c语言视频教程下载 复制本贴地址    
作者: seny11 [seny11]    论坛用户   登录
转载

试试看把!

http://www.chinaitlab.com/itlab_member/video/C_01.rm  
145m
http://www.chinaitlab.com/itlab_member/video/one_miniute1.rm  
68m
http://www.chinaitlab.com/itlab_member/video/mcse_01.asf  
82m


时间: 2004.1.04 4:00:01    标题: [转贴] 循序渐进使用 wxWindows 的指南 

--------------------------------------------------------------------------------

循序渐进使用 wxWindows 的指南
Markus Neifer(markus.neifer@gmx.net)
软件开发人员
2002 年 4 月

将 Windows 应用程序移植到 Linux 不必涉及再培训的痛苦经历。Markus Neifer 演示了如何使用 wxWindows 移植 MFC,指导读者使用 wxWindows 这一开放源码 GUI 工具箱,并循序渐进地向读者介绍了一个完整的移植示例。
您可能仍然在维护用微软基础类库(Microsoft Foundation Classes(MFC))构建的旧的 Windows 应用程序,而现在却有客户要求 Linux 版本,该怎么办呢?在您的团队中可能有技术熟练的 MFC 开发人员,但如何达到加速 Linux 开发呢?别急;本文就是针对您这种情况而写的。依靠 wxWindows(一种用于 C++ 和 Python 的可移植 GUI 工具箱)的帮助,我将以多文档界面(Multiple Document Interface (MDI))文本编辑器为例向您演示如何将仅 Windows 的 MFC 应用程序移植到 Linux。类似这样的小型应用程序有助于我们将讨论集中在移植框架的具体细节上,从而避免我们迷失在代码的汪洋中。可以在本文后面的参考资料一节中获取完整的 MFC 应用程序和 wxWindows 应用程序的源代码。

文档/视图概述
我将演示的应用程序使用众所周知的文档/视图体系结构,因为它可以象大多数应用程序一样处理文档。即使您的应用程序不使用文档/视图体系结构,我也建议您读下去。只要您已在转向这种框架,您就可能想要添加这项功能。

在我的关于 wxWindows 的前一篇文章中,曾经指出过 MFC 和 wxWindows 之间具有某些相似性。字符串类 CString、wxString 和事件系统之间都非常相似。但还不止这些相似性。wxWindows 工具箱还提供对文档/视图体系结构的类 MFC 支持。

我将从核心类的比较开始。下表列出了两种框架的文档/视图体系结构所涉及的类。

表 1. 文档/视图类比较 类 MFC 类 wxWindows 类
文档(Document) CDocument wxDocument
视图(View) CView wxView
编辑视图(Edit view) CEditView n/a
模板类(Template class) CMultiDocTemplate wxDocTemplate
MDI 父框架(MDI parent frame) CMDIFrameWnd wxDocMDIParentFrame
MDI 子框架(MDI child frame) CMDIChildWnd wxDocMDIChildFrame
文档管理器(Document manager) n/a wxDocManager

除编辑视图类以外,每个 MFC 类都有其对应的 wxWindows 类。(最后一项中 MFC 的部分为空,因为 MFC 没有独立的文档管理器类。由应用程序类 CWinApp 内部处理文档。)下列 UML 图演示了这些类之间的关系:

图 1. MFC 类




图 2. wxWindows 类


应用程序
每个框架都提供一个表示应用程序本身的类。MFC 应用程序类声明了一个构造器、一个用于初始化的方法、一个用于事件处理的方法和一个消息映射表。您需要这个消息映射表声明和事件处理方法,因为应用程序的“about”对话框将由该类处理。

应用程序类:MFC
class CPortMeApp : public CWinApp
{
public:
CPortMeApp();
virtual BOOL InitInstance();
afx_msg void OnAppAbout();
DECLARE_MESSAGE_MAP()
};



注:最初创建 MFC 应用程序时,我使用 Microsoft Visual Studio 所包含的应用程序向导来创建,但在我的代码片段中,我将不给出由向导生成的、有时会使人迷惑的注释(//{{AFX_MSG 及类似的东西)。完整的源代码,请参阅 ZIP 压缩文档。

对应的 wxWindows 类看起来略微有些不同。它也声明了一个构造器及一个用于初始化的方法,但却不需要任何东西来处理消息。如同您随后将看到的一样,在主框架类中处理“about”对话框。

应用程序类:wxWindows
class PortedApp : public wxApp
{
public:
PortedApp();
bool OnInit();
int OnExit();
protected:
wxDocManager* m_docManager;
};


正如下面所描述的那样,这个类需要一个 wxDocManager 属性来处理在初始化方法 OnInit() 中创建的模板。应用程序退出时,清理方法 OnExit() 将删除这个 wxDocManager 对象。

所有应用程序都需要其入口点(也称为 main() 或 WinMain())。在实现这一点的方法上,两种框架略微有些不同。在 MFC 中,象这样创建应用程序类的静态对象:

CPortMeApp theApp;

在 wxWindows 中,则象这样使用 IMPLEMENT_APP() 宏:

IMPLEMENT_APP(PortedApp)

如果对该宏所做的事情感兴趣,请查看头文件 wx/app.h 中它的定义,在可下载的源代码中找到该头文件。基本上,它是为所使用的平台插入适当的入口点函数。创建了应用程序类的对象之后,需要对其进行初始化。对于 MFC,Microsoft 建议不使用应用程序对象的构造器来初始化对象。而是应该使用其 InitInstance() 方法。要执行任何清理,请实现 ExitInstance() 方法。

虽然应用程序初始化有很多事情要做,这里我将只着重讨论与文档/视图有关的代码。要建立文档/视图框架,InitInstance() 方法必须创建一个 CMultiDocTemplate,如下所示:

创建文档/视图代码:MFC
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_PORTMETYPE,
RUNTIME_CLASS(CPortMeDoc),
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CPortMeView));



wxWindows 应用程序提供 OnInit() 方法来做任何初始化工作,并提供 OnExit() 用于清理。要在 wxWindows 应用程序中建立文档/视图框架,OnInit() 方法必须象这样创建 wxDocTemplate:

创建文档/视图代码:wxWindows
m_docManager = new wxDocManager();

wxDocTemplate* pDocTemplate;
pDocTemplate = new wxDocTemplate(
m_docManager, "Pom", "*.pom", "", "pom", "Pom Doc", "Text View",
CLASSINFO(PortedDoc),
CLASSINFO(PortedView));



MFC 和 wxWindows 所做的事情基本上相同。框架需要关于哪个文档同哪个视图有关以及这个组合处理哪种文档的信息。类型包括文档的描述性名称以及这类文档的文件扩展名。两种框架都使用模板来处理这一问题(请注意这与标准 C++ 模板没有什么关系)。

MFC CMultiDocTemplate 也保存关于同文档相关联的子框架的信息,并且该模板被添加到管理该模板的应用程序对象中。wxWindows wxDocTemplate 额外需要一个管理模板的 wxDocManager 对象。请记住,wxDocManager 是 wxWindows 应用程序的应用程序类的一个属性。

完成文档/视图框架初始化之后,就创建了应用程序的主框架。在 MFC 应用程序的 InitInstance() 方法中,按如下创建一个主框架:

创建主框架:MFC
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;

m_pMainWnd = pMainFrame;

pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();



调用 pMainFrame->LoadFrame(IDR_MAINFRAME) 从资源文件装入所有关于主框架的信息。

在 wxWindows 中,可以从资源文件建立菜单、对话框以及控件,或者可以在代码中创建它们。我更喜欢在代码中创建它们,但如果您愿意将代码同资源分离,就应该看一看 wxWindows 资源系统(请参阅 wxWindows 文档中的主题概述)。

创建主框架:wxWindows
m_mainFrame = new MainFrame(m_docManager, (wxFrame*) NULL, "DocView Demo",
wxPoint(0, 0), wxSize(500, 400),
wxDEFAULT_FRAME_STYLE);

// Set up menu bar...

m_mainFrame->SetMenuBar(menu_bar);
m_mainFrame->Centre(wxBOTH);
m_mainFrame->Show(TRUE);

SetTopWindow(m_mainFrame);



在查看应用程序初始化完成后发生了什么事情之前,让我向您演示每个框架的文档和视图类。

文档
文档保存应用程序处理的基于文件的数据。它负责从文件装入该数据,并在必要的时候将该数据保存回文件。该 MFC 类的声明类似于这样:

文档类声明:MFC
class CPortMeDoc : public CDocument
{
protected:
CPortMeDoc();
DECLARE_DYNCREATE(CPortMeDoc)

public:
virtual BOOL OnNewDocument();
virtual void Serialize(CArchive& ar);

virtual ~CPortMeDoc();
DECLARE_MESSAGE_MAP()
};



请注意:该类有一个受保护(protected)的构造器。决不要直接创建该类的任何对象;相反,框架使用序列化来创建文档。因此,需要使用 DECLARE_DYNCREATE() 宏。wxWindows 类的声明类似于这样:

文档类声明:wxWindows
class PortedDoc : public wxDocument
{
public:
virtual bool OnSaveDocument(const wxString& filename);
virtual bool OnOpenDocument(const wxString& filename);
virtual bool IsModified() const;
virtual void Modify(bool mod);

private:
DECLARE_DYNAMIC_CLASS(PortedDoc)
};



DECLARE_DYNAMIC_CLASS() 宏是必需的,因为 wxWindows 就象 MFC 一样动态地创建该类的对象。

视图
如果 MFC 应用程序处理文本文档,那么从 CEditView 派生视图类是一个好主意。该类已经在其客户机窗口内提供了基本的编辑功能和文本控件。这里我遵循自己的建议,MFC 视图类的声明类似于这样:

视图类声明:MFC
class CPortMeView : public CEditView
{
protected:
CPortMeView();
DECLARE_DYNCREATE(CPortMeView)

public:
CPortMeDoc* GetDocument();
virtual void OnDraw(CDC* pDC);
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);

virtual ~CPortMeView();
DECLARE_MESSAGE_MAP()
};



在 wxWindows 中,(还)没有专门的编辑视图。但是创建您自己的编辑视图却很容易。只需在视图框架内有一个由 wxTextCtrl 派生的文本控件。视图类将控件和框架都当作类属性来处理。因此 wxWindows 声明类似于这样:

视图类声明:wxWindows
class PortedView : public wxView
{
public:
MyTextCtrl* textsw;

PortedView();

bool OnCreate(wxDocument* doc, long flags);
void OnDraw(wxDC* dc);
bool OnClose(bool deleteWindow = TRUE);

private:
wxMDIChildFrame* CreateChildFrame(wxDocument* doc, wxView* view);

wxFrame* frame;

DECLARE_DYNAMIC_CLASS(PortedView)
};



除了常见的用于窗口创建、窗口重画以及窗口关闭的事件处理程序之外,该类还有一个创建框架并填充其菜单栏的方法。

虽然拥有公共(public)属性不是一个好主意,但是为简单起见,我在这里还是这么做。在您的应用程序中应该避免这么做(我将在以后的版本中除去它)。

主框架
既然已经有了处理和显示数据的文档和视图类,并且也有了处理文档/视图框架的应用程序类,现在需要一个主框架类来同用户交互。同样,两种框架提供类似的功能,而实际实现有略微不同。MFC 主框架类将一个状态栏和一个工具栏作为属性保存,同时 MFC 主框架类还提供一些方法来处理窗口创建,以及声明消息映射表。请注意:该类是从 CMDIFrameWnd 派生而来的,因为该应用程序有一个 MDI 界面。

主框架类:MFC
class CMainFrame : public CMDIFrameWnd
{
public:
CMainFrame();
virtual ~CMainFrame();

virtual BOOL PreCreateWindow(CREATESTRUCT& cs);

protected:
CStatusBar m_wndStatusBar;
CToolBar m_wndToolBar;

afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

DECLARE_MESSAGE_MAP()

private:
DECLARE_DYNAMIC(CMainFrame)
};



wxWindows 主框架类将菜单作为属性保存,还具有创建其工具栏的方法,以及声明一个事件表外加一个处理应用程序的 about 对话框的方法。因为这个应用程序有一个 MDI 界面,所以该类是从 wxDocMDIParentFrame 派生而来。

主框架类:wxWindows
class MainFrame : public wxDocMDIParentFrame
{
public:
wxMenu* editMenu;

MainFrame(wxDocManager* manager, wxFrame* frame, const wxString& title,
const wxPoint& pos, const wxSize& size, long type);

void OnAbout(wxCommandEvent& event);
void RecreateToolbar();

private:
DECLARE_CLASS(MainFrame);
DECLARE_EVENT_TABLE();
};



全部工作原理
移植完成了。我来回顾一下实现这一点所需做的事情。MFC 应用程序类(派生自 CWinApp)移植到派生自 wxApp 的应用程序类,包括初始化代码。将 MFC 文档类(派生自 CDocument)移植到派生自 wxDocument 的文档类。将 MFC 视图类(派生自 CView)移植到派生自 wxView 的文档类。除了这些同文档/视图有关的类之外,将 MFC 主框架类(派生自 CMDIFrameWnd)移植到派生自 wxDocMDIParentFrame 的类。

在其目前的版本中,该应用程序已经能够做很多工作。可以处理多个文本文件。可以打开、编辑并保存这些文件,并且应用程序保持最近打开文件的历史记录。如果不得不从头编写这些功能,那么将需要更多行代码 ― 更多行代码意味着要测试和维护更多行。在两种框架中,都使用了专门的命令标识符来处理常用的同文档/视图有关的命令。常用的文件命令是新建、打开和保存。常用的编辑命令是剪切、复制和粘贴。其它经常使用的命令有打印命令(这个应用程序中还没有实现该命令)和帮助命令。下表列出了这两种框架结构的文档/视图体系结构所包含的命令标识符。

表 2. 标准的命令标识符

MFC wxWindows
ID_FILE_OPEN wxID_OPEN
ID_FILE_CLOSE wxID_CLOSE
ID_FILE_NEW wxID_NEW
ID_FILE_SAVE wxID_SAVE
ID_FILE_SAVE_AS wxID_SAVEAS
ID_EDIT_CUT wxID_CUT
ID_EDIT_COPY wxID_COPY
ID_EDIT_PASTE wxID_PASTE
ID_APP_EXIT wxID_EXIT
ID_EDIT_UNDO wxID_UNDO
ID_EDIT_REDO wxID_REDO
ID_HELP_INDEX wxID_HELP
ID_FILE_PRINT wxID_PRINT
ID_FILE_PRINT_SETUP wxID_PRINT_SETUP
ID_FILE_PRINT_PREVIEW wxID_PREVIEW

窗口管理器
如果您对 Linux 开发真的很感兴趣,则可能已经做了一些研究,发现 Linux 上有不同的窗口管理器。曾经是 MFC 开发人员的您可能觉得这很奇怪。在进行 Windows 开发时,您无须操心不同的窗口管理器,因为只有一个窗口管理器。

开始 wxWindows 开发时,您可能想知道您的应用程序是否将运行在两个主流的窗口管理器 ― K 桌面环境(K Desktop Environment (KDE))和 GNOME ― 之上。我已经在 Microsoft Windows NT 4.0、使用公共桌面环境(Common Desktop Environment (CDE))的 Sun Solaris 2.6 以及使用 KDE 的 Linux 2.2 上使用了 wxWindows 工具箱,没有任何问题。由于 wxWindows 仅仅是一个建立在其它低级别 GUI 工具箱上的高级别层次,所以可以选择如何构建 Linux 应用程序。可以使用 Motif 版本(或者还要更好一些的,免费 Lesstif)或者 wxWindows 的 GTK+ 版本。GTK+ 是 GNOME 使用的基础窗口构件工具箱,但是它在 KDE 下也运行得非常好。我建议您使用 GTK+ 版本,因为它通常更新,有许多开发人员对它进行开发,而且有用户在使用它。因此,如果您希望获得更多帮助,则可以求助于邮件列表或新闻组,询问关于 GTK+ 版本的问题。

下面是前前后后的抓屏,可以让您对移植的应用程序有一个大致的认识。

图 3. 原始的 MFC 应用程序


图 4. Windows 上的 wxWindows 应用程序


图 5. Linux/KDE 上的 wxWindows 应用程序


真实的故事
wxWindows 工具箱不只是书呆子的另一个玩具。它成熟、稳定,并且人们在实际应用程序中用它来解决实际问题。

wxWindows 项目创始人 Julian Smart 目前致力于 Red Hat,他已经将 eCos 配置工具(eCos Configuration Tool)移植到了 wxWindows。eCos 配置工具是一个配置 eCos 嵌入式操作系统的图形工具。人们已将最初的 MFC 应用程序移植到了 wxWindows,尤其是在 Linux 上(参阅参考资料)。 SciTech Software 的人员也已经使用 wxWindows 来全部重新开发用于 SciTech Display Doctor 产品的前端 GUI。IBM 已经获得了 SciTech Display Doctor 的一个专门版本的许可证,IBM 现在将其作为 IBM 所有基于 OS/2 Warp 的操作系统(包括 Warp Client、Workspace On Demand 及 Warp Server for e-business)的主要显示驱动程序来分发。SciTech Software 对 wxWindows 社区做出了积极的贡献,它提交了扩展包 wxApplet、wxUniversal 和 wxMGL。使用 wxMGL,wxWindows 应用程序甚至可以运行在 DOS 和其它嵌入式操作系统之上,例如 QNX、RT-Target、SMX 等等。SciTech Software 全力资助 wxUniversal 和 wxMGL 项目(参阅参考资料)。

结束语
本文演示了使用 wxWindows 工具箱将 MFC 文档/视图框架的 Windows 应用程序移植到 Linux 的基本原理。wxWindows 工具箱同 MFC 框架有一些相似性,从而帮助 MFC 开发人员可以达到加速 Linux 开发。有了这些基础知识,您应该能够将最棒的应用程序移植到 Linux。但不要忘了:wxWindows 不是只能用于 Linux。也许该是考虑在其它 UNIX 或者甚至 Macintosh 上来运行您的应用程序一个版本的时候了。

参考资料

从 Markus 的主页下载本文所演示的原始应用程序和移植的应用程序的完整源代码。


有关 wxWindows 的概述,请阅读 Markus 的文章 细述 wxWindows(developerWorks,2001 年 2 月)。


wxWindows 主页是 wxWindows 社区成员要去的主要场所。它提供关于 wxWindows 的信息及对它的支持,包括下载、邮件列表、样本应用程序以及与 wxWindows 有关的工具。


wxPython 将 Python 脚本语言同 wxWindows GUI 库结合起来。有关 wxPython 的介绍,请阅读 wxPython for newbies(developerWorks,2001 年 3 月)。


Coding with KParts 讨论了 K 桌面环境(K Desktop Environment)图形组件的 KParts 体系结构(developerWorks,2002 年 2 月)。


有关更多关于 K 桌面环境的内容,请访问 KDE 主页。


有关更多关于 GNOME 的信息,请查看 GNOME 主页。


在 Red Hat 站点上可以找到更多关于 Red Hat eCos 配置工具的内容。


可以在 SciTech Software 站点上获取更多关于 SciTech Display Doctor 的信息。


可以在 OS/2 Warp 页面上找到更多关于 OS/2 设备驱动程序包(它使用 SciTech Display Doctor)的信息。


在 developerWorks Linux 专区里可以找到更多 Linux 文章。

关于作者
Markus Neifer 最初使用 LOGO 教学语言来编程,后来他使用过多种 BASIC 语言。在研究地理信息学期间,他学了一些 C,但很快又转向了 C++ 和 Java,因为这两种语言具有面向对象的本质。他曾在研发领域工作过,期间他出版过关于科技软件的面向对象开发的文章。目前,他是地理信息系统领域的软件工程师。可以通过 markus.neifer@gmx.net 和 Markus 联系。




[此贴被 seny11(seny11) 在 04月17日23时02分 编辑过]

地主 发表时间: 04-04-17 22:43

回复: seny11 [seny11]   论坛用户   登录
http://www.20cn.net/cgi-bin/club/showpic.pl?u=seny11&f=mfc.gif http://www.20cn.net/cgi-bin/club/showpic.pl?u=seny11&f=wxWindow.gif


B1层 发表时间: 04-04-17 22:49

回复: seny11 [seny11]   论坛用户   登录

时间: 2003.5.08 12:31:56    标题: 动态汉字串显示技术 
转载
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

  望着屏幕静静"睡着"的字符,我终于开始"嘀咕"起来:现代一切都那么美好,为什么不让它们也欢乐起来呢?和我们共同分享这份美好。因此,我决心让它们动起来。首先呢,使我想到了电视里的流动字幕。我很快便写成了这个LdShowString()函数,它主要是动态显示流动字串的子串,然后再一个一个从左消失。接着,我开始写FromRightToLeftShowString(),将一个字符串中的字符一个一个从右跑到屏幕中央,然后又一个个从左消失;至于FromRightAndLeftToMiddle- ShowString(),仍是将一个字符串,从左和右边一个个跑到中央,然后又从两边飞着消失。

  当然,这些动态显示技术不仅仅如此,在此,只是作者"班门弄斧",希望能得到您的指导与指教。

  #include <conio.h>
  #include <string.h>
  #include <mem.h>
  #include <dos.h>
  #include <stdlib.h>
  #include <stdio.h>
  #define DELAYTIME 3000 /*延时值*/
  #define ESC 0x011b /*ESC键值*/
  
  void SetCursor(int style) /*关闭或打开光标显示*/
  { union REGS r;
  r.h.ah=1;
  r.h.cl=31 ; /*style=0关闭光标,1打开光标*/
  r.h.ch=style? 30:32; /*B5=1表示关闭光标*/
  int86(0x10,&r,&r);
  }
  
  unsigned int GetKey(void) /*取当前的按键值*/
  { if(bioskey(1)) return(bioskey(0));
  else return(0);
  }
  
  /*画一方框*/
  void DrawBox(int x,int y,int w,int h)
  {int xx;
  gotoxy(x,y);printf("┌");
  for(xx=x+1;xx<x+w-1;xx++) printf("─");
  printf("┐");
  for(xx=y+1;xx<y+h-1;xx++)
  { gotoxy(x,xx);printf("│");
  gotoxy(x+w-1,xx);printf("│");
  }
  gotoxy(x,y+h-1);printf("└");
  for(xx=x+1;xx<x+w-1;xx++) printf("─");
  printf("┘");
  }
  /*函数作用:从左到右逐字推出式动态显示字符串
  然后再从左到右消去字符串
  入口参数:s 字符串;y 行坐标
  注意事项:s的长度为能过80,且为偶数,
  中间若有ASCII,用对出现*/
  void FromRightToLeftShowString(int y,char *ss)
  { int i,x,n;char c;
  n=strlen(ss); if (n<2) {printf("askjdlfj");return;}
  do
  { for(i=2;i<=n;i+=2) /*先动态出字符串*/
  { c=ss[i]; ss[i]=0;
  for(x=78;x>=(80-n)/2+(i/2-1)*2;x--)
  {gotoxy(x,y);printf("%2s ",&ss[i-2]);
  delay(DELAYTIME/20);
  if (GetKey()==ESC) { ss[i]=c;return;}
  }
  ss[i]=c;
  }
  delay(DELAYTIME*2);
  for(i=2;i<=n;i+=2) /*再消去字符串*/
  { c=ss[i];ss[i]=0;
  for(x=(80-n)/2+(i/2-1)*2-1;x>=1;x--)
  {gotoxy(x,y);printf("%2s ",&ss[i-2]);
  delay(DELAYTIME/20);
  if (GetKey()==ESC) {ss[i]=c;return;}
  }
  gotoxy(1,y);printf(" ");
  ss[i]=c;
  }
  } while(1);
  }
  
  /*函数作用:从左右同时向中央逐字推出式动态显示字符串,
  再从中央同时向左右消去字符串
  入口参数:s 字符串;y 行坐标
  注意事项:s的长度为能过80,且为偶数,
  中间若有ASCII,需要成对出现*/
  void FromRightAndLeftToMiddleShowString(int y,char *string)
  { int i,li,ri,rx,lx,n,Ln,Rn;
  char s[80];
  n=strlen(string);
  if (n%2 || n>80 ) return;
  while(1)
  { for(i=(n+2)/4-1;i>=0;i--) /*先出汉字*/
  for(lx=1;lx<=(80-n)/2+2*i;lx++)
  {gotoxy(lx,y);
  printf(" %c%c",string[2*i],string[2*i+1]);
  gotoxy(79-lx,y);
  printf("%c%c ",string[2*(n/2-i-1)],string[2*(n/2-i-1)+1]);
  delay(DELAYTIME/20);
  if (GetKey()==ESC) return;
  }
  delay(DELAYTIME*4);
  for(i=0;i<=(n+2)/4-1;i++) /*再消去*/
  {for(lx=(80-n)/2+2*i;lx>=1;lx--)
  {gotoxy(lx,y);
  printf("%c%c ",string[2*i],string[2*i+1]);
  gotoxy(79-lx,y);
  printf(" %c%c",string[2*(n/2-i-1)],string[2*(n/2-i-1)+1]);
  delay(DELAYTIME/20);
  if (GetKey()==ESC) return;
  }
  gotoxy(1,y);printf(" ");
  gotoxy(79,y);printf(" ");
  }
  delay(DELAYTIME*2);
  }
  }
  
  void LdShowString(int x,int y,int w,char *s)
  { /*函数作用:流动显示字符串
  入口参数,y 显示左上角坐标
  w 窗口的宽度(一定要为偶数)
  s 欲显示的字符串
  注意事项:s一定要由连续成对的ASCII字符或全角字符组成
  如:s="abc是英文字母"不正确,但s="abc 是英文
  字母"是正确的. */
  char c;
  int n,i,end;
  strcat(s," "); /*后面加上两个空格*/
  n=strlen(s);
  DrawBox(x-1,y-1,w+2,3);/*画一方框*/
  while(1)
  for(i=2;i<=n+w;i+=2,delay(DELAYTIME))
  { end=min(i,n);
  c=s[end];
  s[end]=0;
  gotoxy(max(w-i+x,x),y);
  cprintf(&s[(i>w)? i-w:0]); /*部分显示该字中*/
  s[end]=c;
  if(GetKey()==ESC) return;
  }
  }
  
  static char *FromRightToLeftStr="此为逐字\
  推出式显示&消去字符串,按ESC退出";
  static char *FromRLToMidStr="此为从左右同\
  时向中央显示字符串,然后从左右消去(ESC\
  退出)";
  static char *LdStr="我忠心祝愿:\
  《电脑爱好者》杂志社的编辑同志们在\
  新的一年里工作顺心、万事如意、心想\
  事成。将《电脑爱好者》越办越好。湖\
  北省浠水师范学校 江龙 1998年\
  5月30日。(此为在窗口中流动显示\
  字符串的模拟程序,按ESC键可退出)";
  main()
  {SetCursor(0); /*关闭光标*/
  clrscr();
  LdShowString(10,12,60,LdStr);
  clrscr();
  FromRightToLeftShowString(12,FromRightToLeftStr);
  clrscr();
  FromRightAndLeftToMiddleShowString(12,FromRLToMidStr);
  SetCursor(1); /*打开光标*/
  }


B2层 发表时间: 04-04-17 22:56

回复: seny11 [seny11]   论坛用户   登录
标题: 走近C++之父Bjarne Strustrup 

--------------------------------------------------------------------------------

myan
编者按:很荣幸,本文中翻译的内容及照片均得到了这位C++之父Bjarne Stroustrup博士的允许,也感谢这位前辈的对本刊祝福“Good Luck with your magazine”。博士的主页地址是http://www.research.att.com/~bs/。

Bjarne Stroustrup博士,1950年出生于丹麦,先后毕业于丹麦阿鲁斯大学和英国剑桥大学,AT&T大规模程序设计研究部门负责人,AT&T、贝尔实验室和ACM成员。
(编者注:Stroustrup博士的简历可参考http://www.research.att.com/~bs/bio.html)

1979年,B. S开始开发一种语言,当时称为“C with Class”,后来演化为C++。1998年,ANSI/ISO C++标准建立,同年,B. S推出了其经典著作The C++ Programming Language的第三版。C++的标准化标志着B. S博士倾20年心血的伟大构想终于实现。但计算机技术的发展一日千里,就在几年前人们还猜想C++最终将一统天下,然而随着Internet的爆炸性增长,类似Java、C#等新的、现代感十足的语言咄咄逼人,各种Script语言更是如雨后春笋纷纷涌现。在这种情况下,人们不禁有些惶恐不安。C++是不是已经过时了呢?其前景如何?标准C++有怎样的意义?应该如何学习?我们不妨看看B. S对这些问题的思考。以下文字是译者从Stroustrup主页上发表的FAQ中精选出来的。(原文可参考http://www.research.att.com/~bs/bs_faq.html及http://www.research.att.com/~bs/bs_faq2.html)

请谈谈C++书。
没有,也不可能有一本书对于所有人来说都是最好的。不过对于那些真正的程序员来说,如果他喜欢从“经典风格”的书中间学习一些新的概念和技术,我推荐我的The C++ Programming Language, 1998年的第三版和特别版。那本书讲的是纯而又纯的C++,完全独立于平台和库(当然得讲到标准库)。该书面向那些有一定经验的程序员,帮助他们掌握C++,但不适合毫无经验的初学者入门,也不适合那些临时程序员品尝C++快餐。所以这本书的重点在于概念和技术,而且在完整性和精确性上下了不少功夫。如果你想知道为什么C++会变成今天的模样,我的另一本书The Design and Evolution of C++ 能给你满意的答案。理解设计的原则和限制能帮助你写出更好的程序。http://www.accu.com/是最好的书评网站之一,很多有经验的程序员在此仗义执言,不妨去看看。
(编者注:The C++ Programming Language的特别版比第三版多了两篇附录:本地化Locales和标准库的异常安全Standard-Library Exception Safety。这两篇附录可在Stroustrup博士的主页上下载。)

学习C++要花多长时间?
这要看你说的“学习”是什么意思了。如果你是一个Pascal程序员,你应该能很快地使你的C++水平达到与Pascal相近的程度;而如果你是一个C程序员,一天之内你就能学会使用C++进行更出色的C风格编程。另一方面,如果你想完全掌握C++的主要机制,例如数据抽象,面向对象编程,通用编程,面向对象设计等等,而此前又对这些东西不很熟悉的话,花上个一两年是不足为奇的。那么是不是说这就是学习C++所需要的时间呢?也许再翻一番,我想打算成为更出色的设计师和程序员最起码也要这么长的时间。如果学习一种新的语言不能使我们的工作和思想方式发生深刻的变革,那又何苦来哉?跟成为一个钢琴家或者熟练掌握一门外语相比,学习一种新的、不同的语言和编程风格还算是简单的。

了解C是学习C++的先决条件吗?
否!C++中与C相近的子集其实比C语言本身要好学,类型方面的错误会少一些,也不像C那样绕圈子,还有更好的支持库。所以应该从这个子集开始学习C++。

要想成为真正的OO程序员,我是不是得先学习Smalltalk?
否。如果你想学Smalltaok,尽管去学。这种语言很有趣,而且学习新东西总是一个好主意。但是Smalltalk不是C++,而且把Smalltalk的编程风格用在C++里不会有什么好结果。如果你想成为一个出色的C++程序员,而且也没有几个月的时间百无聊赖,请你集中力量学好C++以及其背后的思想。

我如何开始学习C++?
这取决于你的基础和学习动机。如果你是个初学者,我想你最好找个有经验的程序员来帮助你,要不然你在学习和实践中不可避免的犯下的种种错误会大大地打击你的积极性。另外,即使你的编译器配备了充足的文档资料,一本C++书籍也永远是必不可少的,毕竟文档资料不是学习编程思想的好教材。
选择书籍时,务必注意该书是不是从一开始就讲授标准C++,并且矢志不渝地使用标准库机制。例如,从输入中读取一个字符串应该是这样的:
string s; // Standard C++ style
cin >> s;
而不是这样的:
char s[MAX]; /* Standard C style */ scanf("%s",s);
去看看那些扎实的C++程序员们推荐的书吧。记住,没有哪本书对所有人来说都是最好的。
另外,要写地道的C++程序,而避免用C++的语法写传统风格的程序,新瓶装旧酒没多大意义。
(遗憾的是,目前在市面上的中文C++教材中,符合B. S的这个标准的可以说一本都没有,大家只好到网上找一些英文的资料来学习了。――译者)

怎样改进我的C++程序?
不好说。这取决于你是怎么使用该语言的。大多数人低估了抽象类和模板的价值,反过来却肆无忌惮地使用造型机制(cast)和宏。这方面可以看看我的文章和书。抽象类和和模板的作用当然是提供一种方便的手段建构单根的类层次或者重用函数,但更重要的是,它们作为接口提供了简洁的、逻辑性的服务表示机制。

语言的选择是不是很重要?
是,但也别指望奇迹。很多人似乎相信某一种语言能够解决他们在系统开发中遇到的几乎所有问题,他们不断地去寻找完美的编程语言,然后一次次的失败,一次次的沮丧。另外一些人则将编程语言贬为无关紧要的细节,把大把大把的银子放在开发流程和设计方法上,他们永远都在用着COBOL, C和一些专有语言。一种优秀的语言,例如C++,能帮助设计者和程序员做很多事情,而其能力和缺陷又能够被清楚地了解和对待。

ANSI/ISO标准委员会是不是糟蹋了C++?
当然不是!他们(我们)的工作很出色。你可以在一些细节上找些歪理来挑刺,但我个人对于这种语言以及新的标准库可是欣欣然。ISO C++较之C++的以前版本更出色更有条理。相对于标准化过程刚刚开始之初,你今天可以写出更优雅、更易于维护的C++程序。新的标准库也是一份真正的大礼。由于标准库提供了strings, lists, vectors, maps以及作用于其上的基本算法,使用C++的方式已经发生了巨大的变化。

你现在有没有想删除一些C++特性?
没有,真的。问这些问题的人大概是希望我回答下面特性中的一个:多继承、异常、模板和RTTI。但是没有它们,C++就是不完整的。在过去的N年中,我已经反复考虑过它们的设计,并且与标准委员会一起改进了其细节,但是没有一个能被去掉又不引起大地震。
从语言设计的角度讲,我最不喜欢的部分是与C兼容的那个子集,但又不能把它去掉,因为那样对于在现实世界里工作的程序员们来说伤害太大了。C++与C兼容,这是一项关键的设计决策,绝对不是一个叫卖的噱头。兼容性的实现和维护是十分困难的,但确实使程序员们至今受益良多。但是现在,C++已经有了新的特性,程序员们可以从麻烦多多的C风格中解脱出来。例如,使用标准库里的容器类,象vector, list, map, string等等,可以避免与底层的指针操作技巧混战不休。

如果不必和C兼容,你所创造的语言是不是就会是Java?
不是,差得远。如果人们非要拿C++和Java来作比较,我建议他们去阅读The Design and Evolution of C++,看看C++为什么是今天这个样子,用我在设计C++时遵从的原则来检验这两种语言。这些原则与SUN的Java开发小组所持的理念显然是不同的。除了表面语法的相似性之外,C++与Java是截然不同的语言。在很多方面,Java更像Smalltalk(译者按:我学习Java时用的是Sun的培训教材,里面清楚地写道:Java在设计上采用了与C++相似的语法,与Smalltalk相似的语义。所以可以说Java与C++是貌合神离,与Smalltalk才是心有灵犀)。Java语言相对简单,这部分是一种错觉,部分是因为这种语言还不完整。随着时间的推移,Java在体积和复杂程度上都会大大增长。在体积上它会增长两到三倍,而且会出现一些实现相关的扩展或者库。这是一条每个成功的商业语言都必须走过的发展之路。随便分析一种你认为在很大范围内取得了成功的语言,我知道肯定是无有例外者,而且实际上这非常有道理。
上边这段话是在Java 1.1推出之前写的。我确信Java需要类似模板的机制,并且需要增强对于固有类型的支持。简单地说,就是为了基本的完整性也应该做这些工作。另外还需要做很多小的改动,大部分是扩展。1998年秋,我从James Gosling(Java语言的创始人――译者)那里得到一份建议书,说是要在Java中增加固有类型、操作符重载以及数学计算支持。还有一篇论文,是数学分析领域的世界级大师,伯克利大学的W. Kahan教授所写的How Java’s Floating-Point Hurts Everyone Everywhere(“且看Java的浮点运算如何危害了普天下的芸芸众生”――译者),揭露了Java的一些秘密。
我发现在电视和出版物中关于Java的鼓吹是不准确的,而且气势汹汹,让人讨厌。大肆叫嚣凡是非Java的代码都是垃圾,这是对程序员的侮辱;建议把所有的保留代码都用Java重写,这是丧心病狂,既不现实也不负责任。Sun和他的追随者似乎觉得为了对付微软罪恶的“帝国时代”,就必须如此自吹自擂。但是侮辱和欺诈只会把那些喜欢使用不同编程语言的程序员逼到微软阵营里去。
Java并非平台无关,它本身就是平台。跟Windows一样,它也是一个专有的商业平台。也就是说,你可以为Windows/Intel编写代码,也可以为Java/JVM编写代码,在任何一种情况下,你都是在为一个属于某个公司的平台写代码,这些代码都是与该公司的商业利益扯在一起的。当然你可以使用任何一种语言,结合操作系统的机制来编写可供JVM执行的程序,但是JVM之类的东西是强烈地偏向于Java语言的。它一点也不像是通用的、公平的、语言中立的VM/OS。
私下里,我会坚持使用可移植的C++作大部分工作,用不同的语言作余下的工作。
(”Java is not platform-independent, it is the platform”,B. S的这句评语对于C++用户有着很大的影响,译者在国外的几个新闻组里看到,有些C++高手甚至把这句话作为自己的签名档,以表明对Java的态度和誓死捍卫C++的决心。实际上有很多程序员不光是把自己喜爱的语言当成一种工具,更当成一种信仰。――译者)

您怎么看待C#语言?
就C#语言本身我没什么好说的。想让我相信这个世界还需要另外一个专有的语言可不是一件容易的事,而且这个语言还是专门针对某一个专有操作系统的,这就更让我难以接受。直截了当地说,我不是一个专有语言的痴迷者,而是一个开放的正式标准的拥护者。

在做大项目时,您是不是真的推荐Ada,而不是C++?
当然不是。我不知道这是谁传出来的谣言,肯定是一个Ada信徒,要么是过分狂热,要么是不怀好意。

你愿不愿意将C++与别的语言比较?
抱歉,我不愿意。你可以在The Design and Evolution of C++的介绍性文字里找到原因。
有不少书评家邀请我把C++与其它的语言相比,我已经决定不做此类事情。在此我想重申一个我很久以来一直强调的观点:语言之间的比较没什么意义,更不公平。主流语言之间的合理比较要耗费很大的精力,多数人不会愿意付出这么大的代价。另外还需要在广泛的应用领域有充分经验,保持一种不偏不倚、客观独立的立场,有着公正无私的信念。我没时间,而且作为C++的创造者,在公正无私这一点上我永远不会获得完全的信任。
人们试图把各种语言拿来比较长短,有些现象我已经一次又一次地注意到,坦率地说我感到担忧。作者们尽力表现的公正无私,但是最终都是无可救药地偏向于某一种特定的应用程序,某一种特定的编程风格,或者某一种特定的程序员文化。更糟的是,当某一种语言明显地比另一种语言更出名时,一些不易察觉的偷梁换柱就开始了:比较有名的语言中的缺陷被有意淡化,而且被拐弯抹角地加以掩饰;而同样的缺陷在不那么出名的语言里就被描述为致命硬伤。类似的,有关比较出名的语言的技术资料经常更新,而不太出名的语言的技术资料往往是几年以前的,试问这种比较有何公正性和意义可言?所以我对于C++之外的语言的评论严格限制在一般性的特别特定的范畴里。
换言之,我认为C++是大多数人开发大部分应用程序时的最佳选择。

别人可是经常拿他们的语言与C++比来比去,这让你感到不自在了吗?
当这些比较不完整或者出于商业目的时,我确实感觉不爽。那些散布最广的比较性评论大多是由某种语言,比方说Z语言的拥护者发表的,其目的是为了证明Z比其它的语言好。由于C++被广泛地使用,所以C++通常成了黑名单上的头一个名字。通常,这类文章被夹在Z语言的供货商提供的产品之中,成了其市场竞争的一个手段。令人震惊的是,相当多的此类评论引用那些在开发Z语言的公司中工作的雇员的文章,而这些经不起考验文章无非是想证明Z是最好的。特别是在这些比较中确实有一些零零散散的事实,(所以更具欺骗性――译者)毕竟没有一种语言在任何情况下都是最好的。C++当然不完美,不过请注意,特意选择出来的事实虽然好像正确,但有时是完全的误导。
以后再看到语言比较方面的文章时,请留心是谁写的,他的表述是不是以事实为依据,以公正为准绳,特别是评判的标准是不是对于所引述的每一种语言来说都公平合理。这可不容易做到。

在做小项目时,C优于C++吗?
我认为非也。除了由于缺乏好的C++编译器而导致的问题之外,我从没有看到哪个项目用C会比用C++更合适。(不过现在C++编译器导致的问题还是不可忽略的,当你看到同样功能的C++程序可执行代码体积比C大一倍而且速度慢得多时,会对此有所感触的。――译者)


B3层 发表时间: 04-04-17 23:02

回复: seny11 [seny11]   论坛用户   登录
STL(Standard Template Library:标准模板库)为用模板实现的代码可重用性和可扩充性提供了算法和容器。在函数描述和避免off-by-one错误方面,这里有两个技巧可以帮助你更有效的完成STL。

  如何编写STL中没有定义的函数
许多JAVA爱好者批评STL在std::string模板中没有包含一些十分有用的函数。之所以将这些函数排斥在外是因为你可以很容易自己编写它们。例如,你可以这样实现一个将所有数据类型转换为字符串型的to_string函数:

template< class T>
inline std::string to_string( const T & Value)
{
std::stringstream streamOut;
streamOut << Value;
return streamOut.str( );
}

// 对原数据类型为string类型的特殊处理
template< >
inline std::string to_string( const std::string & Value)
{
return Value;
}

该函数对所有C++内有数据类型均适用。对支持“<<”运算符的类同样适用。

当然,实现与to_string功能相反的函数也是容易的。下面一小段代码将解释这一点。

template< class T>
inline T from_string( const std::string & ToConvert)
{
std::stringstream streamIn( ToConvert);
T ReturnValue = T( );
streamIn >> ReturnValue;
return ReturnValue;
}

from_string函数将字符串转化为某一类型。例如,它可以将字符串转化为数据。如果输入有效,本函数将转换并返回它。另外,该函数返回指定的类型的默认值,T()为默认的构造函数。还有,它适用与任何C++内有的数据类型,对任何默认构造函数实现了“>>”操作符的类也同样适用。

注意from_string函数假定你转换的字符串中仅有一个值,任何多余的值都将被忽略。如果字符串有多值并且都是你需要的,请勿使用form_string函数,你可以从流中(直接)读取它们。

下面的例子说明了如何使用上述的两个函数:

int n = 4;
std::string str = to_string( n); // "4"
long l = 534587;
str = to_string( l); // "534587"
n = from_string< int>( "a"); // 0
int nTest = from_string< int>( "5744"); // 5744
int nTest2 = from_string< int>( "57y4"); // 57
int nTest3 = from_string< int>( "5743 65"); // 5743; 65 ignored !!!
return 0;

避免off-by-one错误
bug经常是由于你忘了检查数组的最后一个元素,或者对数组进行越界读写造成的(后一种情况更加严重)。这两种错误在不符合分工编程标准的情况下常常会出现。

只有一种方法可以避免off-by-one错误:在循环中使用[start_of_seq,end_of_seq]类型。就是说你可以使用从序列开始可元素(start_of_seq)一直到(但不包括)序列结束的那个元素(end_of_seq)。对空序列同样如此。这个指导原则是受STL启发而得到的。STL中的迭代器是这样工作的:你总是有两个容器begin()和end()前者包含start_of_seq而后者包含了end_of_seq。

上面所述的遍历(Iterating)序列的方法是很容易理解的。举例来说:

StringsArray::const_iterator itFirst = astrNames.begin();
StringsArray::const_iterator itLast = astrNames.end();
while ( itFirst != itLast)
{
std::cout << *itFirst << std::endl;
++ itFirst;
}

然而,你不可能一直使用STL。开发者经常不得不处理旧有的代码或者应用程序接口(API)。为了在任何循环中遵循上述约定,就要尽量避免使用“<=”操作符。这样,通过下标来访问一个数组时就很容易了:

for ( int idx = 0; idx < nNamesCount; ++idx)
{
std::cout << astrNames[ idx] << std::endl;
}

如果你知道了数组的下标的上下界,尽量使用下界的后一个下标:

int idxStart = idxFirst;//数组的上届
int idxEnd = idxLast + 1;//数组的下界的后一个下标
for ( int idx = idxStart; idx < idxEnd; ++idx)
{
std::cout << astrNames[ idx] << std::cout;
}

你也可以通过指针来实现:

const std::string * pStrStart = GetFirstName();
const std::string * pStrEnd = GetLastName() + 1;
while ( pStrStart < pStrEnd)
{
std::cout << pStrStart->c_str() << std::endl;
}

B4层 发表时间: 04-04-17 23:09

回复: Idof [idof]   论坛用户   登录
看过了

B5层 发表时间: 04-04-18 16:45

论坛: 编程破解

20CN网络安全小组版权所有
Copyright © 2000-2010 20CN Security Group. All Rights Reserved.
论坛程序编写:NetDemon

粤ICP备05087286号