所谓非功能性需求,是指软件产品为满足用户业务需求而必须具有且除功能需求以外的特性。软件产品的非功能性需求包括系统的性能、可靠性、可维护性、可扩充性和对技术和对业务的适应性等。下面对其中的某些指标加以说明。在这里可以看到非功能性需求涉及的范围很广,软件产品本身不是孤立存在的,还涉及到诸多外在环境的影响。非功能性需求必须考虑软件既要可用,又要易用。

对于非功能性需求描述的困难在于很难像功能性需求那样,可以通过结构化和量化的词语来描述清楚,在描述这类需求时候我们经常采用软件性能要好,查询要在多少时间内出结果,软件健壮性要好等较模糊的描述词语。这类描述词语都是脱离了软件的执行环境,人和相关的场景的描述,因此信息很难体现到软件架构设计和具体的实现中。我们在架构设计中关注的安全,系统开发框架,并发和性能,异常日志等不是凭空产生出来的,而是来源于我们对非功能性需求的分析。

一个软件系统必须完整,因此不仅仅包括了可执行的程序,还包括了在线帮助,数据和用户管理,日志异常查询,自动升级等相关功能特征。这些需求不仅仅是为了满足用户的需要,也是为了我们后续维护和监控系统的需要。

系统的可靠性,可维护性和适应性是密不可分的。当系统出现故障和用户出现错误的操作后是否支持恢复,当用户在使用过程中遇到错误的时候是否可以立即定位问题,但业务场景和逻辑发生变化的时候系统是否支持,当网络不稳定或使用中异常中断的情况下系统是否都有相应的容错措施,这些都是需要在非功能性需求中考虑到的问题。

易用性也是我们在开发非功能性需求中必须要考虑到的问题,易用性同时还涉及到美工和UI界面,人机工程,交互式设计,心理学,用户行为模式等多方面的知识。易用性的三原则就是易见,易学和易用或者叫为发现,易懂,效率。易见就是各种功能操作不要藏得太深,用户很容易找到他们期望进行的各种操作;易学需要软件系统通过在线帮助,导航,向导等各种方式保证软件是可自学习的;易用的重点则在软件在熟练使用后应该可以更快的进行各项操作。这三者相互间也存在冲突,需要平衡,而平衡的一个重点就是真正的做到以用户为中心进行设计,需要去细分场景和用户。

对于非功能性需求的描述,在描述过程中必须要强调到人,业务场景,环境等各方面的内容。强调的目的就是要说明非功能性需求不是无限度的,任何一项非功能性需求的实现往往会付出更大的研发人力成本和硬件网络成本。比如我们在描述一个表单的模糊查询功能的时候,如果简单的描述为所有查询都要在多少秒内完成,那么这种需求将很难得到满足,以下是一些可选的描述方式。


1.估计用户数为1万人,每天登录用户数为3000左右,网络的带宽为100M带宽。
2.在非高峰时间根据编号和名称特定条件进行搜索,可以在3秒内得到搜索结果。
3.当通过互联网接入系统的时候,期望在编号和名称搜索时最长查询时间<15秒。


有了这些场景和数据后,我们在进行架构设计的时候就可以有针对性的选择我们的开发框架和模式,数据库,软硬件环境配置已经复杂功能的具体实现方式等。同时这些需求还可以更好的指导我们对通过性能测试等工具对这些非功能性需求进行验证。

posted @ 2010-06-23 22:38 landylee 阅读(173) 评论(0) 编辑

GB2312:

一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,
这样我们就可以组合出大约7000多个简体汉字了。
在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,
连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,
而原来在127号以下的那些就叫"半角"字符了。
一个汉字算两个英文字符!一个汉字算两个英文字符……

GBK:

不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,
不管后面跟的是不是扩展字符集里的内容。
结果扩展之后的编码方案被称为 GBK 标准,
GBK 包括了 GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。

GB18030:

又加了几千个新的少数民族的字,GBK 扩成了 GB18030。

中国的程序员们看到这一系列汉字编码的标准是好的,于是通称他们叫做 "DBCS"(Double Byte Charecter Set 双字节字符集)。
在DBCS系列标准里,
最大的特点是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里,
因此他们写的程序为了支持中文处理,必须要注意字串里的每一个字节的值,
如果这个值是大于127的,那么就认为一个双字节字符集里的字符出现了。


每个国家都搞出自己的一套编码,为了全世界的使用统一,于是有了。。。


ISO (国际标谁化组织)的国际组织决定着手解决这个问题。
他们打算叫它"Universal Multiple-Octet Coded Character Set",简称 UCS, 俗称 "UNICODE"。

UNICODE:
ISO 就直接规定必须用两个字节,也就是16位来统一表示所有的字符,
对于ascii里的那些“半角”字符,UNICODE 包持其原编码不变,只是将其长度由原来的8位扩展为16位,
而其他文化和语言的字符则全部重新统一编码。
由于"半角"英文符号只需要用到低8位,所以其高 8位永远是0,
因此这种大气的方案在保存英文文本时会多浪费一倍的空间。


他们的strlen函数靠不住了,一个汉字不再是相当于两个字符了,而是一个!
是的,从 UNICODE 开始,无论是半角的英文字母,还是全角的汉字,
它们都是统一的"一个字符"!同时,也都是统一的"两个字节",
“字节”是一个8位的物理存贮单元,而“字符”则是一个文化相关的符号。
在UNICODE 中,一个字符就是两个字节。


从 Windows NT 开始,MS 趁机把它们的操作系统改了一遍,
把所有的核心代码都改成了用 UNICODE 方式工作的版本,
从这时开始,WINDOWS 系统终于无需要加装各种本土语言系统,就可以显示全世界上所有文化的字符了。

但是,UNICODE 在制订时没有考虑与任何一种现有的编码方案保持兼容,
这使得 GBK 与UNICODE 在汉字的内码编排上完全是不一样的,
没有一种简单的算术方法可以把文本内容从UNICODE编码和另一种编码进行转换,
这种转换必须通过查表来进行。


UNICODE 是用两个字节来表示为一个字符,他总共可以组合出65535不同的字符,
这大概已经可以覆盖世界上所有文化的符号。如果还不够也没有关系,ISO已经准备了UCS-4方案,
说简单了就是四个字节来表示一个字符,样我们就可以组合出21亿个不同的字符出来(最高位有其他用途),
这大概可以用到银河联邦成立那一天吧!


UNICODE 来到时,一起到来的还有计算机网络的兴起,UNICODE 如何在网络上传输也是一个必须考虑的问题,
于是面向传输的众多 UTF(UCS Transfer Format)标准出现了,
顾名思义,UTF8就是每次8个位传输数据,而UTF16就是每次16个位,
只不过为了传输时的可靠性,从UNICODE到 UTF时并不是直接的对应,而是要过一些算法和规则来转换。


从网上引来一段从UNICODE到UTF8的转换规则:

Unicode
UTF-8

0000 - 007F
0xxxxxxx

0080 - 07FF
110xxxxx 10xxxxxx

0800 - FFFF
1110xxxx 10xxxxxx 10xxxxxx


例如"汉"字的Unicode编码是6C49。6C49在0800-FFFF之间,
所以要用3字节模板:1110xxxx 10xxxxxx 10xxxxxx。
将6C49写成二进制是:0110 1100 0100 1001,
将这个比特流按三字节模板的分段方法分为0110 110001 001001,
依次代替模板中的x,得到:1110-0110 10-110001 10-001001,
即E6 B1 89,这就是其UTF8的编码。

当一个软件打开一个文本时,它要做的第一件事是决定这个文本究竟是使用哪种字符集的哪种编码保存的。软件一般采用三种方式来决定文本的字符集和编码:
检测文件头标识,提示用户选择,根据一定的规则猜测
最标准的途径是检测文本最开头的几个字节,开头字节 Charset/encoding,如下表:
EF BB BF UTF-8
FE FF UTF-16/UCS-2, little endian
FF FE UTF-16/UCS-2, big endian
FF FE 00 00 UTF-32/UCS-4, little endian.
00 00 FE FF UTF-32/UCS-4, big-endian.


ANSI字符集:ASCII字符集,以及由此派生并兼容的字符集,
如:GB2312,正式的名称为MBCS(Multi-Byte Chactacter System,多字节字符系统),
通常也称为ANSI字符集。

big endian和little endian
big endian和little endian是CPU处理多字节数的不同方式。
例如“汉”字的Unicode编码是6C49。那么写到文件里时,
究竟是将6C写在前面,还是将49写在前面?
如果将6C写在前面,就是big endian。
还是将49写在前面,就是little endian。

从ASCII、GB2312、GBK到GB18030,这些编码方法是向下兼容的,
即同一个字符在这些方案中总是有相同的编码,后面的标准支持更多的字符。


UTF-8就是以8位为单元对UCS进行编码。

UTF-16以16位为单元对UCS进行编码。
对于小于0x10000的UCS码,UTF-16编码就等于UCS码对应的16位无符号整数。
对于不小于 0x10000的UCS码,定义了一个算法。
不过由于实际使用的UCS2,或者UCS4的BMP必然小于0x10000,
所以就目前而言,可以认为UTF -16和UCS-2基本相同。
但UCS-2只是一个编码方案,UTF-16却要用于实际的传输,
所以就不得不考虑字节序的问题。

我很早前就发现Unicode、Unicode big endian和UTF-8编码的txt文件的开头会多出几个字节,
分别是FF、FE(Unicode),FE、FF(Unicode big endian),EF、BB、BF(UTF-8)。
但这些标记是基于什么标准呢?

即读取文件时,
若为unicode时,前两个字节可以不用读取。注:固定是两个字节一个字。
若为utf-8时,前三个字节可以不用读取。
若为ansi时,前面没有多余字节,可从文件头直接读取。

posted @ 2010-06-03 15:42 landylee 阅读(429) 评论(1) 编辑

我是在VC下编译运行。
1.首先下载TinyXML库的文件,这里给出链接
http://prdownloads.sourceforge.net/tinyxml/tinyxml_2_3_4.zip?download
2.下载后解压这个压缩包,把所有的东西放到一个找的着的地方(比如,E:\开发库\TinyXML)
3.Visual C++(推荐VC++.NET2003)创建一个新的工程(Win32控制台)
4.TinyXML的目录里面找到tinystr.h, tinyxml.h, tinystr.cpp, tinyxml.cpp, tinyxmlerror.cpp, tinyxmlparser.cpp六个文件加入到刚刚创建的项目中去
5.打开tinyxml.h, 在第一行加入下面这行:
#define TIXML_USE_STL //
标志使用STL的内容
6.然后创建一个cpp文件,输入下面的内容:
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include "tinyxml.h"
using namespace std;
 
int _tmain(int argc, _TCHAR* argv[])
{
//原先代码是直接加载XML文件,我作了一下修改,把内容读到字符串后再解析,实际使用时就去掉读取XML文件这一步
string filename = "first.xml";
//TiXmlDocument* doc = new TiXmlDocument(filename.c_str());
     //////////////////////////////////////////////////////////////////////////
// 在这里复制文件
//////////////////////////////////////////////////////////////////////////
std::ifstream ifs(filename.c_str());
char buffer[1024];
char c, *p = buffer;
while(ifs.get(c))
{
 *p++=c;
}
*p = 0;
ifs.close();
//////////////////////////////////////////////////////////////////////////
 
//这里开始从字符串中解析XML
//创建TiXmlDocument对象
TiXmlDocument* doc = new TiXmlDocument();
//解析
if(!doc->Parse(buffer))
{
 cout << doc->ErrorDesc() << endl;
}
//获取根节点
const TiXmlElement* root = doc->RootElement();
//循环获取该根节点下面的节点
for( const TiXmlNode* child = root->FirstChild();
 child;
 child=child->NextSibling())
{
 //判断为元素类型并且是staticbox元素,Value()获取该标签的名称
 if((child->Type() == TiXmlNode::ELEMENT) && (!strcmp(child->Value(),"staticbox")))
 {
   const TiXmlElement *box = (const TiXmlElement*)child;
 
   double px, py, pz;
   double dx, dy, dz;
 
 //获取属性值
   std::string mesh;
   mesh = box->Attribute("mesh");
  //继续循环获取子节点相关数据
   for(const TiXmlNode *sub_tag = box->FirstChild(); sub_tag; sub_tag = sub_tag->NextSibling() )
   {
    if(sub_tag->Type() == TiXmlNode::ELEMENT)
    {
     const TiXmlElement *sub_element = (const TiXmlElement*)sub_tag;
 
     if(!strcmp(sub_tag->Value(),"position"))
     {
      px = (sub_element->Attribute("x",&px))?px:0.0;
      py = (sub_element->Attribute("y",&py))?py:0.0;
      pz = (sub_element->Attribute("z",&pz))?pz:0.0;
     }
     else if(!strcmp(sub_tag->Value(),"dimension"))
     {
      dx = (sub_element->Attribute("x",&dx))?dx:1.0;
      dy = (sub_element->Attribute("y",&dy))?dy:1.0;
      dz = (sub_element->Attribute("z",&dz))?dz:1.0;
     }
else if(!strcmp(sub_tag->Value(),"test"))
     {
         //使用GetText()方法来获取该标签的值,如这里获取的是test的值1和2
          string temp = sub_element->GetText();
     }
    }
   }
 
   cout << "<StaticBox>\n";
   cout << "\tPosition = (" << px << ", " << py << ", " << pz << ")\n";
   cout << "\tDimension = (" << dx << ", " << dy << ", " << dz << ")\n\n";
 }
}
 
delete doc;
getchar();
     return 0;
}
 7.然后在项目的文件夹中加入一个xml文件,如下:
<?xml version="1.0" encoding="utf-8" ?>
<Scene>
<staticbox mesh="crate.mesh">
 <position x="-8" y="2" z="4" />
 <dimension x="2" y="4" z="2" />
 <test>1</test>
</staticbox>
<staticbox mesh="crate.mesh">
 <position x="3" y="2" z="4" />
 <dimension x="2" y="4" z="2" />
 <test>2</test>
</staticbox>
</Scene>
8.编译运行
 
附另一个TinyXML读写XML的例子,此处写XML是直接写入XML文件,如果要使用这里的函数不写入文件而是组织成XML流,需要相应修改代码。代码在VS2005中测试运行成功。
#define TIXML_USE_STL //标志使用STL的内容
 
#include "stdafx.h"
#include <iostream>
#include <sstream>
#include <fstream>
#include "tinyxml.h"
using namespace std;
 
int WriteXML() ;
int ReadXML() ;
//读取内存里的一段XML
int ReadXML2() ;
 
int _tmain(int argc, _TCHAR* argv[])
{
 
     cout << "开始测试!" <<endl;
   
    WriteXML() ;
    ReadXML() ;
    ReadXML2() ;
    cout << "结束测试!" << endl ;
   
     return 0;
}
 
int WriteXML()
{
    TiXmlDocument xmlDoc( "test.xml" ); // 建立一个XML文件
   
    TiXmlDeclaration Declaration( "1.0","gb2312", "yes" ); // 声明XML的属性
   
    xmlDoc.InsertEndChild( Declaration ); // 写入基本的XML头结构  
   
    TiXmlNode * pNode = NULL;
    TiXmlElement* pRootElm = NULL;
    TiXmlElement* pChildeElm = NULL;
    TiXmlElement* pItemElm = NULL;
    TiXmlText* pText = NULL; // 一个指向Text的指针
    pText = new TiXmlText("good") ;
   
    pRootElm = new TiXmlElement( "todo" );
   
    pNode = xmlDoc.InsertEndChild(*pRootElm) ;
    pRootElm = pNode->ToElement() ;
   
    pChildeElm = new TiXmlElement("child1") ;
    pChildeElm->SetAttribute("num", 9) ;
   
    pNode = pRootElm->InsertEndChild(*pChildeElm) ;
    TiXmlElement* pChildeElm1 = NULL;
    pChildeElm1 = pNode->ToElement() ;
    pChildeElm1->InsertEndChild(*pText) ;
   
    delete pChildeElm ;
    pChildeElm = NULL ;
    pChildeElm = new TiXmlElement("child2") ;
    pChildeElm->SetAttribute("num", 10) ;
   
    pNode = pRootElm->InsertBeforeChild (pChildeElm1, *pChildeElm) ;
    TiXmlElement* pChildeElm2 = NULL;
    pChildeElm2 = pNode->ToElement() ;
    pChildeElm2->InsertEndChild(*pText) ;
   
   
    xmlDoc.Print() ;
 
   
    xmlDoc.SaveFile(); // 把XML文件写入硬盘
   
    return 0 ;
}
 
 
int ReadXML()
{
    cout<<endl ;
    cout<<"开始read XML"<<endl ;
    TiXmlDocument xmlDoc( "test.xml" );
    xmlDoc.LoadFile() ;
   
    TiXmlElement* xmlRootElement = 0 ;
    TiXmlElement* xmlSubElement = 0 ;
    TiXmlNode * pNode = NULL;
   
    pNode = xmlDoc.FirstChild("todo") ;
    xmlRootElement = pNode->ToElement() ;
   
    if(xmlRootElement)
    {
        pNode = xmlRootElement->FirstChild("child1") ;
        xmlSubElement = pNode->ToElement() ;
        cout<<xmlSubElement->Value()<<endl ;
        cout<<xmlSubElement->Attribute("num")<<endl ;
        cout<<xmlSubElement->GetText()<<endl ;
    }
    else
    {
        cout<<"找不到根元素"<<endl ;
    }
   
    cout<<"结束read XML"<<endl ;
    return 0 ;
}
 
int ReadXML2()
{
    cout<<endl ;
    cout<<"开始read XML"<<endl;
     const char* demoEnd ="<?xml version=\"1.0\" encoding=\"gb2312\" standalone=\"yes\" ?>"
                         "<todo>"
                         "<child2 num=\"10\">good</child2>"
                         "<child1 num=\"9\">good</child1>"
                         "</todo>" ;
    TiXmlDocument xmlDoc;
 
    xmlDoc.Parse( demoEnd ) ;
   
    TiXmlElement* xmlRootElement = 0 ;
    TiXmlElement* xmlSubElement = 0 ;
    TiXmlNode * pNode = NULL;
   
    pNode = xmlDoc.FirstChild("todo") ;
    xmlRootElement = pNode->ToElement() ;
   
    if(xmlRootElement)
    {
        pNode = xmlRootElement->FirstChild("child1") ;
        xmlSubElement = pNode->ToElement() ;
        cout<<xmlSubElement->Value()<<endl ;
        cout<<xmlSubElement->Attribute("num")<<endl ;
        cout<<xmlSubElement->GetText()<<endl ;
    }
    else
    {
        cout<<"找不到根元素"<<endl ;
    }
   
    cout<<"结束read XML"<<endl ;
    return 0 ;
}
//----------------------------mycode--------------------------------//
private:
template <class T>
static T * _tinyxmlparser(const TiXmlElement *_element,T *_fieldsptr);
public:
static  void * xmlparser(const string &xmlStream,int layers=1);
//---------------------------------cpp----------------------------------//

template <class T>
T * Util::_tinyxmlparser(const TiXmlElement *_element,T *_fieldsptr)
{
 const TiXmlNode *child = _element->FirstChild();
 if (!child || child->Type() != TiXmlNode::ELEMENT)
 {
  string str_value = _element->GetText()==NULL?"":_element->GetText();
  _fieldsptr->insert(typename T::value_type(_element->Value(),str_value));
 }
 else
 {
  for(const TiXmlElement *sub_tag =_element->FirstChildElement(); sub_tag; sub_tag = sub_tag->NextSiblingElement())
  {
   _tinyxmlparser(sub_tag,_fieldsptr);
  } 
 }
 return _fieldsptr;
}
void * Util::xmlparser(const std::string &xmlStream,int layers)
{
 try
 {
  //创建TiXmlDocument对象
  TiXmlDocument doc;// = TiXmlDocument;
  //解析
  const char *buffer =  xmlStream.c_str();
  if(!doc.Parse(buffer))
  {
   throw MyError("xml_error.log",doc.ErrorDesc());
  }
  //获取根节点
  const TiXmlElement* root = doc.RootElement();
  switch(layers)
  {
  case 1:
   {
    fields_t * _fields = new map<string,string>;
    _tinyxmlparser(root,_fields);
    return _fields;
   }
   break;
  default:
   {
    multimap<string,string> *  _fields = new multimap<string,string>;
    _tinyxmlparser(root,_fields);
    return _fields;
   }
   break;
  }

 }
 catch(...)
 {
  return NULL;
 }
}

 

posted @ 2010-06-01 14:50 landylee 阅读(619) 评论(0) 编辑


在 IIS 7.0 中,您可以创建网站、Web 应用程序和虚拟目录,以便与 Internet、Intranet 或 Extranet 上的用户共享信息。网站、Web 应用程序和虚拟目录以一种分层结构的关系协同进行工作,用作寄存联机内容的基础构建块。

简而言之,网站包含一个或多个 Web 应用程序,Web 应用程序包含一个或多个虚拟目录,虚拟目录则映射到 Web 服务器或远程计算机上的物理目录。下面的小节将分别详细介绍这三个概念。

什么是网站?
网站是 Web 应用程序的容器,您可以通过一个或多个唯一绑定来访问网站。网站绑定由 IP 地址、端口和可选的主机头组合而成,HTTP.sys 在此侦听对网站的请求。

什么是 Web 应用程序?
Web 应用程序是一种在应用程序池中运行并通过 HTTP 协议向用户提供 Web 内容(通常以 HTML 格式)的软件程序。创建 Web 应用程序时,Web 应用程序的名称将成为网站 URL 的一部分,用户可以通过 Web 浏览器请求该 URL。

在 IIS 7.0 中,每个网站必须拥有一个必需的 Web 应用程序,它被称为根 Web 应用程序或默认 Web 应用程序。但网站可以包含多个 Web 应用程序。例如,您可能有一个在线商务网站,该网站包含若干 Web 应用程序,例如购物车应用程序和登录应用程序,前者允许用户在购物过程中收集商品,后者允许用户在购买时回溯已保存的支付信息。

什么是虚拟目录?
虚拟目录是您在 IIS 中指定并映射到本地或远程服务器上的物理目录的目录名称。然后,目录名称将成为 Web 应用程序 URL 的一部分,用户可以通过 Web 浏览器请求该 URL 以访问物理目录的内容,例如网页或其他目录和文件的列表。如果为虚拟目录指定了不同于物理目录的名称,将增加用户在服务器上查找实际物理文件结构的难度,因为 URL 无法直接映射到网站的根。

在 IIS 7.0 中,每个 Web 应用程序都必须拥有一个命名为根虚拟目录的虚拟目录,该虚拟目录可以将 Web 应用程序映射到包含 Web 应用程序内容的物理目录。但 Web 应用程序可以拥有多个虚拟目录。例如,如果您希望 Web 应用程序包含文件系统的其他位置上的图像,但又不希望将这些图像文件移动到映射至 Web 应用程序根虚拟目录的物理目录中,则可以使用虚拟目录。
转载请注明出自高地网 - 笔记本技术区 http://enbbs.gaodi.net/,本贴地址:http://enbbs.gaodi.net/viewthread.php?tid=1616

 

 

posted @ 2010-05-19 22:07 landylee 阅读(361) 评论(0) 编辑
之前写过一个关于symbian常用程序架构的帖子,不过当时是在简单的看了看书本的条件下写的,仅仅是当时做的一个笔记而已,并没有多少自己的感想体会在里面。当然现在也不能就对传统的symbian os架构很了解了,因为毕竟没有做太多深入的研究试验,仅仅是根据自己的想法做了几个小程序而已。
 
从四个小程序上来分析:
 
一、在屏幕上显示两个Label
这是最最基本的了。
关键的就是创建一个容器Container(实际上就是一个拥有窗口的复合控件而已)和一个UI其中Container继承自控件基类CCoeControl,而UI继承自CAknAppUi,这都是必须的。另外,如果Container里的控件,比如编辑框想接收用户事件的话,那么Container同时还要继承?????????因为我的容器里面只有两个Label,所以也就不需要继承这个类了。
(1)先看容器类Container的内容:
因为容器Container里包含两个Label,一定要注意,将两个Label 的指针设为Container类的私有成员变量即:
private:
     CEikLabel* iLabel;
     CEikLabel* iToDoLabel;
当然这是在Container类的头文件里声明的。
然后就是看Container的四个方法,这四个都需要我们重写,否则容器中的两个Label控件不会显示。它们分别是:
void Draw(const TRect& aRect) const;
virtual CCoeControl* ComponentControl(TInt aIndex) const;
virtual TInt CountComponentControls() const;
void SizeChanged();
 
在本例中,我是这样重写这四个方法的:
CCoeControl* CHelloWorldBasicAppView::ComponentControl(TInt aIndex) const
{
     switch(aIndex)
     {
     case 0:
         return iLabel;
     case 1:
         return iToDoLabel;
     default:
         return NULL;
     }
}
获取容器中每个控件的指针。
 
void CHelloWorldBasicAppView::SizeChanged()
{
     //设置标签的位置
     iLabel->SetExtent( TPoint(10,10), iLabel->MinimumSize() );
    iToDoLabel->SetExtent( TPoint(10,100), iToDoLabel->MinimumSize() );
    
}
SetExtent()方法设置Label的位置。
开始时,我认为,Label的大小不会改变,所以不需要重写该方法,但事实证明,即使容器中的控件大小不会改变,我们也必须重写这两个方法。如果不,那么这两个Label就不会显示。
 
TInt CHelloWorldBasicAppView::CountComponentControls() const
{
     return 2;
}
返回容器中控件的数目,在这里有两个Label,所以返回2。
 
void CHelloWorldBasicAppView::Draw(const TRect& aRect) const
{
    // Get the standard graphics context
    CWindowGc& gc = SystemGc();
    // Gets the control's extent
    TRect rect = Rect();
    // Clears the screen
    gc.Clear(rect);
     //设置笔刷
gc.SetPenStyle( CGraphicsContext::ENullPen );
    gc.SetBrushColor( KRgbRed ); //红色
    gc.SetBrushStyle( CGraphicsContext::ESolidBrush );
    gc.DrawRect( aRect );
 }
将整个窗口的aRect范围,涂成红色。通过一个TRect对象可以设置Draw的区域即:gc.DrawRect( aRect );也就是说,并不一定要绘制整个窗口。
 
下面看Container的ConstructL方法:
void CHelloWorldBasicAppView::ConstructL(const TRect& aRect)
    {
     // Create a window for this application view
     CreateWindowL();//复合控件创建窗口
 
     iLabel=new(ELeave) CEikLabel;
     iLabel->SetContainerWindowL(*this);//将不拥有窗口的控件和窗口关联
     iLabel->SetTextL(_L("iLabel"));
     iLabel->SetUnderlining(ETrue);
 
     iToDoLabel=new(ELeave) CEikLabel;
     iToDoLabel->SetContainerWindowL(*this);
     iToDoLabel->SetTextL(_L("iToDoLabel"));
 
    // Set the windows size
    SetRect(aRect);
 
    // Activate the window, which makes it ready to be drawn
    ActivateL();
}
首先,我们给这个容器创建一个窗口,然后分别创建两个Label的实例iLabel和iToDoLabel,这其中用到一个方法SetContainerWindowL(*this);,表示将这两个控件显示在那个窗口上,而参数表示的是容器Container,而不是一个Rwindow,这实际上是调用的是CcoeControl中的方法:
virtual void SetContainerWindowL(const CCoeControl& aContainer);
通过看这个函数原型就知道了没有必要非要传递一个Rwindow实例,我们也可以传递一个拥有窗口的容器,而在这里容器Container就是一个拥有窗口的CCoeControl实例,因为我们调用了方法CreateWindowL();
当然也可以给该方法传递一个RWindow实例,即调用CCoeControl的重载方法:
void SetContainerWindowL(RWindow& aWindow);
 
然后设置我们创建的窗口的显示范围SetRect(aRect);,一般是全屏,当然我们也可以设置一个TRect实例,比如:TRect(TPoint(0,0),TSize(100,100)),但是这种情况下,就会看到,我们程序的窗口没有占满整个屏幕,也就是说,仅有TRect(TPoint(0,0),TSize(100,100))的区域来显示我们的程序,而屏幕上剩余的范围还会显示原来的,这样子就比较奇怪,比较难看了。
 
最后激活窗口ActivateL();也就是说调用容器的Draw()、SizeChanged()等函数,从而开始绘制窗口。
 
最后就是容器Container类的析构函数:
CHelloWorldBasicAppView::~CHelloWorldBasicAppView()
{
     if(iLabel)
     {
         delete iLabel;
          iLabel=NULL;
     }
     if(iToDoLabel)
     {
         delete iToDoLabel;
          iToDoLabel=NULL;
     }
}
这个析构函数的主要作用是销毁我们容器中创建的两个Label实例iLabel和iToDoLabel。
 
(2)看UI的内容
其实主要就是UI的ConstructL()方法和析构函数,之所以有析构函数,因为实际上UI也是一个C类。
void CHelloWorldBasicAppUi::ConstructL()
{
    BaseConstructL();
    iAppView = CHelloWorldBasicAppView::NewL(ClientRect());
iAppView->SetMopParent( this );
    AddToStackL(iAppView);
}
首先创建UI的框架即调用方法BaseConstructL();,然后创建容器Container实例,实际上就是这里的iAppView。
接下来的的SetMopParent( this );和AddToStackL(iAppView);在本例中并不是必须的:
SetMopParent( this );是为了设置父控件用的。据网友pan讲:通过这个方法可以设置控件之间的父子关系,然后在子控件中就可以访问父控件或其他子控件,父控件中也可以访问子控件。需要注意的就是要讲这个方法和SetContainerWindowL()区分开。
AddToStackL(iAppView);将容器放到栈顶,从而可以接收用户的事件,如果想让其他容器接收事件的话,就可以通过另一个方法,RemoveFromStatck(iAppView)将当前容器从栈顶移出,然后在将其他容器移入该栈顶即:AddToStackL(iAppView2);
 
CHelloWorldBasicAppUi::~CHelloWorldBasicAppUi()
{
    if (iAppView)
        {
        iEikonEnv->RemoveFromStack(iAppView);
        delete iAppView;//调用容器Container的析构函数,销毁容器对象即iAppView
        iAppView = NULL;//注意,一定要将指针置空,否则就会称为悬空指针,也就是野指针。
        }
}
在析构函数中销毁创建的容器实例。
 
需要注意:容器实例iAppView必须是UI类的私有成员,即:
private:
       CHelloWorldBasicAppView* iAppView;
 
小结:这里面出现几个概念,比如屏幕、窗口、容器、复合控件等,比较容易让人混淆。我的理解是这样子的:屏幕就只有一个,也就是手机的显示屏,是一个物理概念;而窗口是逻辑上的概念,一个程序里我们可以创建多个窗口,下面的一个例子我就来说明这个问题;容器和复合控件都是继承自控件基类CCoeControl,并且它们的类定义中都有子控件成员,不同之处就是容器拥有自己的窗口,而复合控件没有,我们可以把复合控件加到其他的复合控件或容器上,我也会在下面的例子里加以实现。
 
二、屏幕上面切换显示两个窗口
每个窗口都有两个Label。
最开始,我是想这样来实现:让第一个iAppView创建一个窗口,而第二个iAppView2(实际上就是一个复合控件)不再创建新的,而是共用iAppView创建的窗口。试验的时候,在UI的ConstructL()方法里,先创建iAppView,在将拥有窗口的容器iAppView作为参数传递到iAppView2的构造函数中,但却发现并不能实现。
因此,我只能也给iAppView2也创建一个窗口,看这两个容器类的构造函数如下:
void CHelloWorldBasicAppView::ConstructL(const TRect& aRect)
    {
    // Create a window for this application view
    CreateWindowL();//复合控件创建窗口
 
     iLabel=new(ELeave) CEikLabel;
     iLabel->SetContainerWindowL(*this);//将不拥有窗口的控件和窗口关联
     iLabel->SetTextL(_L("iLabel"));
     iLabel->SetUnderlining(ETrue);
 
     iToDoLabel=new(ELeave) CEikLabel;
     iToDoLabel->SetContainerWindowL(*this);
     iToDoLabel->SetTextL(_L("iToDoLabel"));
 
    // Set the windows size
    SetRect(aRect);
 
    // Activate the window, which makes it ready to be drawn
    ActivateL();
}
 
void CHelloWorldBasicAppView2::ConstructL(const TRect& aRect,CHelloWorldBasicAppView* iAppView)
    {
    // Create a window for this application view
    CreateWindowL();//复合控件创建窗口
 
     iLabel2=new(ELeave) CEikLabel;
     iLabel2->SetContainerWindowL(*iAppView);//将不拥有窗口的控件和窗口关联
     iLabel2->SetTextL(_L("iLabel2"));
     iLabel2->SetUnderlining(ETrue);
 
     iToDoLabel2=new(ELeave) CEikLabel;
     iToDoLabel2->SetContainerWindowL(*iAppView);
     iToDoLabel2->SetTextL(_L("iToDoLabel2"));
 
    // Set the windows size
    SetRect(aRect);
 
    // Activate the window, which makes it ready to be drawn
    ActivateL();
}
通过对照发现,两个构造函数中都创建了窗口,并进行了激活。注意就是它们的窗口范围都是整个屏幕。
现在的问题是:如何轮换显示两个窗口呢?
可以通过方法:iAppView->MakeVisible( EFalse );或iAppView->MakeVisible( ETrue );来实现窗口的是否显示,来在一个屏幕上切换显示两个范围都是占满个屏幕的窗口。
即看UI的构造函数:
void CHelloWorldBasicAppUi::ConstructL()
    {
    BaseConstructL();
    iAppView = CHelloWorldBasicAppView::NewL(ClientRect());
iAppView->MakeVisible( EFalse );//暂时不显示容器iAppView
iAppView2 = CHelloWorldBasicAppView2::NewL(ClientRect());
    }
三、在一个屏幕上同时显示两个窗口
在设定窗口的范围上,iAppView占屏幕的上半部分,而iAppView2占屏幕的下半部分。其他的和上面一样,当然也就不需要MakeVisible()这个方法了,呵呵。
主要的区别就是UI的构造函数:
void CHelloWorldBasicAppUi::ConstructL()
    {
    BaseConstructL();
TRect rect = ClientRect();
    iAppView = CHelloWorldBasicAppView::NewL(TRect(rect.iTl.iX,
                                                           rect.iTl.iY,
                                                           rect.Width(),
                                                           rect.Height()/2+rect.iTl.iY));
 
     iAppView2 = CHelloWorldBasicAppView2::NewL(TRect(rect.iTl.iX,
                                                              rect.Height()/2+rect.iTl.iY,
                                                              rect.Width(),
                                                              rect.iBr.iY));
}
 
这两个容器的窗口分别占屏幕的上下各一半。
 
四、让iAppView2仅作为一个复合控件,并称为容器iAppView的一个子控件,和iAppView在一个窗口上显示。
这个地方要比上面复杂一些,我在这里贴出iAppView2的ConstructL()代码:
void CHelloWorldBasicAppView2::ConstructL(const TRect& aRect)
    {
     iLabel2=new(ELeave) CEikLabel;
     iLabel2->SetContainerWindowL(*this);//将不拥有窗口的控件和窗口关联
     iLabel2->SetTextL(_L("iLabel2"));
     iLabel2->SetUnderlining(ETrue);
 
     iToDoLabel2=new(ELeave) CEikLabel;
     iToDoLabel2->SetContainerWindowL(*this);
     iToDoLabel2->SetTextL(_L("iToDoLabel2"));
}
 
可以看到,仅仅是创建了两个Label控件而已,而没有之前的创建窗口CreateWindowL()和激活窗口ActiveWindowL()方法了,因为这里的iAppView2仅仅是作为一个复合控件而已,所以就不需要创建窗口了。其它的方法即SizeChanged()、Draw()、CountComponentControls()和ComponentControl()都是不变的。
上面的iLabel2->SetContainerWindowL(*this);,参数传递的是当前复合控件对象,虽然复合控件并不拥有窗口,但是通过复合控件iAppView2的SetContainerWindowL()方法中的参数是拥有窗口的iAppView,也就是通过层层上传,最后,这两个Label控件也和要显示在上面的窗口联系起来了。而不是把iAppView直接传递给这两个Label的SetContainerWindowL()方法,这里必须注意。
 
因为要将复合控件iAppView2作为容器iAppView的一个子控件,所以iAppView2要作为iAppView的容器类的一个成员变量即:
private:
     CEikLabel* iLabel;
     CEikLabel* iToDoLabel;
     CHelloWorldBasicAppView2* iAppView2;//多了复合控件iAppView2这个私有成员变量。
因为iAppView的容器类多了一个新的子控件,即iAppView2,所以这个类的ConstructL()、SizeChanged()、CountComponentControls()、ComponentControl()以及析构函数~ChelloWorldBasicAppView都需要修改,如下:
 
void CHelloWorldBasicAppView::ConstructL(const TRect& aRect)
    {
    // Create a window for this application view
     CreateWindowL();//复合控件创建窗口
 
     iLabel=new(ELeave) CEikLabel;
     iLabel->SetContainerWindowL(*this);//将不拥有窗口的控件和窗口关联
     iLabel->SetTextL(_L("iLabel"));
     iLabel->SetUnderlining(ETrue);
 
     iToDoLabel=new(ELeave) CEikLabel;
     iToDoLabel->SetContainerWindowL(*this);
     iToDoLabel->SetTextL(_L("iToDoLabel"));
     //多了下面两行代码
     iAppView2=CHelloWorldBasicAppView2::NewL(TRect(TPoint(0,100),TSize(200,200)));
     iAppView2->SetContainerWindowL(*this);
    
    // Set the windows size
    SetRect(aRect);
 
    // Activate the window, which makes it ready to be drawn
    ActivateL();
}
 
――――――――――――――――――――――――――――――――――――
void CHelloWorldBasicAppView::SizeChanged()
{
     //设置控件的位置
iLabel->SetExtent( TPoint(10,10), iLabel->MinimumSize() );
iToDoLabel->SetExtent( TPoint(10,30), iToDoLabel->MinimumSize() );
//多了下面一行代码,TPoint(10,60)相对于窗口左上角
iAppView2->SetExtent( TPoint(10,60), iToDoLabel->MinimumSize() );
    
}
 
―――――――――――――――――――――――――――――――――――――――――――
TInt CHelloWorldBasicAppView::CountComponentControls() const
{
     return 3;
}
返回是3,不是4(虽然复合控件有两个子控件)。
――――――――――――――――――――――――――――――――――――――――――
CCoeControl* CHelloWorldBasicAppView::ComponentControl(TInt aIndex) const
{
     switch(aIndex)
     {
     case 0:
         return iLabel;
     case 1:
         return iToDoLabel;
     //多了下面两行
     case 2:
         return iAppView2;
     default:
         return NULL;
     }
}
 
――――――――――――――――――――――――――――――――――――
CHelloWorldBasicAppView::~CHelloWorldBasicAppView()
{
     if(iLabel)
     {
         delete iLabel;
          iLabel=NULL;
     }
 
     if(iToDoLabel)
     {
         delete iToDoLabel;
          iToDoLabel=NULL;
     }
     //多了下面几行
     if(iAppView2)
     {
         delete iAppView2;
          iAppView2=NULL;
     }
}
 
 
问题:
1、 draw()方法,不需要做任何改变。我开始想,可能需要加一个iAppView2.DrawNow()之类的代码,启动绘制复合控件iAppView2的代码,实际是多此一举的。系统会自动运行复合控件的draw()等四个方法。
2、  复合控件以及复合控件中的两个Label子控件显示的坐标问题,一定记住,它们都是相对于窗口左上角的坐标位置,我曾经将两个子控件的坐标设为相对于它们所属的复合控件了,结果因为超出屏幕范围,没有显示出来。另外屏幕的范围大约在(0,0)--(120,150)之间,如果设置坐标时超出这个范围,就显示不出来了。
 
当然这只是一些最基本的架构问题,也没有显示一些比较复杂的控件,在这里只是起个熟悉传统程序架构的作用,不正之处,还请各位网友指正。
posted @ 2010-05-19 22:05 landylee 阅读(201) 评论(0) 编辑

  二阶段构造是Symbian中的一个重要的内存处理机制,是Symbian软件开发者所必需掌握的知识。
  本文通过三个问题及其相应解答来解析Symbian中二阶段的基本原理和使用方法,希望对大家的学习有所帮助。
问题1:为什么需要二阶段构造?
  首先考虑如下的语句:
  CClassName* ptr = new (ELeave) CClassName();
  在内存有足够空间的情况下,代码首先在堆上分配一个CClassName类型的对象,并将地址赋给ptr指针,然后调用类的构造函数初始化这个对象。
这样,如果类的构造函数出现了异常,则会发生问题,这种异常发生时没有任何指针指向成功分配给CClassName对象的内存区域,因此这些内存成为孤立内存,发生内存泄漏。这就引出了symbian内存处理的一个重要规则:构造函数绝对不能异常退出。
 
问题2:为什么二阶段函数能够避免内存泄漏?
  二阶段构造函数,顾名思义就是将一个对象的构造分为两个阶段:
  第一个阶段是常规的的构造函数,在该构造函数中,没有可能导致异常退出的代码;
  第二个阶段是可能会产生异常的构造阶段,实现为函数ConstructL();
  这样,对象的构造过程就应当包括了如下的代码:
  CClassName* self = new (ELeave) CClassName();
  CleanupStack::PushL(self);
  self->ConstructL();
  CleanupStack::Pop(self);
  这样的构造方式为什么就能够避免内存泄漏呢?下面我们来逐行分析代码:
  CClassName* self = new (ELeave) CClassName();
  重载的运算符new首先将内存分配给新的self实例,如果分配失败,那么程序异常退出,如果成功给新的对象分配了内存,那么接着执行不会异常退出的第一阶段构  造函数;
  CleanupStack::PushL(self);
  接着我们将本地指针self推入清除栈,因为下面要调用可能发生异常的退出函数。
  self->ConstructL();
  如果该二阶段构造函数在执行时异常退出,那么新的CClassName的指针由清楚栈负责清楚,避免了内存泄漏;另外,如果该函数没有异常退出,则拥有了一个完  全构造的CClassName实例。
  CleanupStack::Pop(self);
  安全的将本地指针从清除栈中弹出;
  每实例化一个对象就要写上述代码确实有些啰嗦了,Symbian OS为了简化实例化的步骤,又引入了NewL(),NewLC()两个函数(其实也可以写成一个NewL(),然而    大家都比较推崇同时创建NewL()和NewLC()),其具体的实现方式见问题3;
 
问题3:如何在新的类中创建二阶段构造函数?
.h头文件:
Class CClassName : public CBase
{
public:
       static CClassName* NewL();
       static CClassName* NewlC();
       ~CClassName();
private:
       CClassName(); //第一阶段构造
       void ConstructL(); //第二阶段构造
      
……
}
 
cpp源文件:
CClassName* CClassName::NewL()
{
       CClassName* self = CClassName::NewLC();
       CleanupStack::Pop(self);
       return self;
}
CClassName* CClassName::NewLC()
{
       CClassName* self = new (ELeave) CClassName();
       CleanupStack::PushL(self);
       self->ConstructL(); //二阶段构造
       return self;
}
void CClassName::ConstructL()
{
/**************可能产生异常的代码************/
}


出处:http://blog.csdn.net/Atoric/archive/2008/05/26/2481631.aspx

posted @ 2010-05-16 10:33 landylee 阅读(191) 评论(0) 编辑
摘要: S60应用程序的运行依赖于大量的OS组件,例如屏幕绘图和应用程序数据持久性等,可以直接使用OS的窗口服务器或者文件服务器即可。      应用程序核心框架类  应用程序框架由一套核心类组成,这些类是所有应用程序框架的基础。这些类封装了应用程序和所需OS服务器之间的相互作用。  第一层:CBase和CActive两个基类,其中CActive也是派生于CBase,而CActive又被第二层的CConE...阅读全文
posted @ 2010-05-12 20:48 landylee 阅读(101) 评论(0) 编辑
摘要: 一、基本类:1 应用程序视图(View)类:GUI根控件,该类实现主窗口,并充当其他应用程序控件的容器。 2 应用程序用户界面(UI)类:该类实例化应用程序View类,处理发自应用程序的命令; 3 应用程序文档类(Application document)类: 该类处理应用程序中非GUI数据-应用程序数据。它还实例化应用程序的UI类; 4 应用类(Application)类:主要的应用程序类,通过...阅读全文
posted @ 2010-05-12 20:42 landylee 阅读(153) 评论(0) 编辑
摘要: 1,冒泡法: public class BubbleSortImpl1 {public static void BubbleSort(int A[]) { int n = A.length; for(int i=0;i<n;i++){ for(int j=0;j<n-i-1;j++){ if(A[j]>A[j+1]) { int temp=A[j]; A[j]=A[j+1]; A...阅读全文
posted @ 2010-05-12 14:21 landylee 阅读(213) 评论(0) 编辑
摘要: 开发symbian的GUI应用是有模板的,用Carbide C++的工程向导可以自动生成程序的基本框架。不过可能与安装的SDK版本有关,我现在只能生成一个AppUi一个Container的常规模板(以前用2nd FP3时还可以生成多个view多个container的视图模板)。不管是哪一种模板,它们共同的部分是入口函数、Application和Document。即以下三个文件是所有工程都具有的:1...阅读全文
posted @ 2010-05-12 14:18 landylee 阅读(627) 评论(0) 编辑