2009年5月12日星期二

select into 和 insert into select 两种表复制语句

select * into destTbl from srcTbl
insert into destTbl(fld1, fld2) select fld1, 5 from srcTbl
以上两句都是将 srcTbl 的数据插入到 destTbl,但两句又有区别的:

第一句(select into from)要求目标表(destTbl)不存在,因为在插入时会自动创建。
第二句(insert into select from)要求目标表(destTbl)存在,由于目标表已经存在,所以我们除了插入源表(srcTbl)的字段外,还可以插入常量,如例中的:5。

2009年5月11日星期一

理解虚基类、虚函数与纯虚函数的概念

http://www.cnblogs.com/MS-Frank/archive/2008/01/16/1041310.html

关于构造函数 析构函数和虚函数的关系收藏

构造函数不能声明为虚函数的原因是:
解释一:所谓虚函数就是多态情况下只执行一个,而从继承的概念来讲,总是要先构造父类对象,然后才能是子类对象,如果构造函数设为虚函数,那么当你在构造父类的构造函数时就不得不显示的调用构造,还有一个原因就是为了防错,试想如果你在子类中一不小心重写了个跟父类构造函数一样的函数,那么你的父类的构造函数将被覆盖,也即不能完成父类的构造.就会出错.

解释二:虚函数的主要意义在于被派生类继承从而产生多态. 派生类的构造函数中, 编译器会加入构造基类的代码, 如果基类的构造函数用到参数, 则派生类在其构造函数的初始化列表中必须为基类给出参数, 就是这个原因.



最近有人问构造函数能不能是虚函数: 当然不能 解释一下: 1,从存储空间角度 虚函数对应一个vtable,这大家都知道,可是这个vtable其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数。 2,从使用角度 虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以构造函数没有必要是虚函数。 =: 虚函数的作用在于通过父类的指针或者引用来调用它的时候能够变成调用子类的那个成员函数。而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。 三. 构造函数不需要是虚函数,也不允许是虚函数,因为创建一个对象时我们总是要明确指定对象的类型,尽管我们可能通过实验室的基类的指针或引用去访问它 但析构却不一定,我们往往通过基类的指针来销毁对象。这时候如果析构函数不是虚函数,就不能正确识别对象类型从而不能正确调用析构函数。四: 从实现上看,vbtl在构造函数调用后才建立,因而构造函数不可能成为虚函数 从实际含义上看,在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象生命期只执行一次,不是对象的动态行为,也没有太大的必要成为虚函数 五: 当一个构造函数被调用时,它做的首要的事情之一是初始化它的V P T R。因此,它只能知道它是“当前”类的,而完全忽视这个对象后面是否还有继承者。 当编译器为这个构造函数产生代码时,它是为这个类的构造函数产生代码- -既不是为基类,也不是为它的派生类(因为类不知道谁继承它)。 所以它使用的V P T R必须是对于这个类的V TA B L E。而且,只要它是最后的构造函数调用,那么在这个对象的生命期内, V P T R将 保持被初始化为指向这个V TA B L E。但如果接着还有一个更晚派生的构造函数被调用,这个构造函数又将设置V P T R指向它的 V TA B L E,等.直到最后的构造函数结束。V P T R的状态是由被最后调用的构造函数确定的。这就是为什么构造函数调用是从基类到更加派生 类顺序的另一个理由。 但是,当这一系列构造函数调用正发生时,每个构造函数都已经设置V P T R指向它自己的 V TA B L E。如果函数调用使用虚机制,它将只产生通过它自己的V TA B L E的调用,而不是最后的V TA B L E(所有构造函数被 调用后才会有最后的V TA B L E)。
析构函数设为虚函数的作用:
解释:在类的继承中,如果有基类指针指向派生类,那么用基类指针delete时,如果不定义成虚函数,派生类中派生的那部分无法析构。

#include "stdafx.h"

#include "stdio.h"

class A
{
public:

A();
virtual ~A();

};
A::A()
{

}

A::~A()
{

printf("Delete class AP\n");

}
class B : public A
{
public:
B();
~B();

};

B::B()
{

}

B::~B()
{
printf("Delete class BP\n");
}
int main(int argc, char* argv[])
{
A *b=new B;
delete b;
return 0;

}

输出结果为:Delete class B
Delete class A

如果把A 的virtual 去掉:
那就变成了Delete class A

2009年5月10日星期日

C++ 的构造/析构/赋值/拷贝函数比较

构造函数、析构函数与赋值函数是每个类最基本的函数。每个类只有一个析构函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数)和多个赋值函数(除了同类的赋值以外,还有其他的赋值方法)。对于任意一个类A,如果不想编写上述函数,C++编译器将自动为A产生四个缺省的函数,如
A(void); // 缺省的无参数构造函数
A(const A &a); // 缺省的拷贝构造函数
~A(void); // 缺省的析构函数
A & operate =(const A &a); // 缺省的赋值函数

有几个需要注意的内容:
@ 构造函数与析构函数的另一个特别之处是没有返回值类型
@ 构造从类层次的最顶层的基类开始,在每一层中,首先调用基类的构造函数,然后调用成员对象的构造函数。析构则严格按照与构造相反的次序执行,在析构的时候,最低层的派生类的析构函数最开始被调用,然后调用每个基类的析构函数。
@ “缺省的拷贝构造函数”和“缺省的赋值函数”均采用“位拷贝”而非“值拷贝”的方式来实现,倘若类中含有指针变量,这两个函数注定将出错

下面通过例子进一步说明,


1.构造函数的初始化表
设存在两个类:
class A
{

A(void); // 无参数构造函数
A(const A &other); // 拷贝构造函数
A & operate =( const A &other); // 赋值函数
virtual ~A(void); //析构函数
};
class B
{
public:
B(const A &a); // B的构造函数

private:
A m_a; // 成员对象
};

下面是B的构造函数的2个实现,其中第一个的类B的构造函数在其初始化表里调用了类A的拷贝构造函数,从而将成员对象m_a初始化;而第二个B的构造函数在函数体内用赋值的方式将成员对象m_a初始化。我们看到的只是一条赋值语句,但实际上B的构造函数干了两件事:先暗地里创建m_a对象(调用了A的无参数构造函数),再调用类A的赋值函数,将参数a赋给m_a。
B::B(const A &a)
: m_a(a)
{

}

B::B(const A &a)
{
m_a = a;

}


2.拷贝函数和构造函数的区别
拷贝构造函数是在对象被创建时调用的,而赋值函数只能被已经存在了的对象调用。
String a(“hello”);
String b(“world”);
String c = a; // 调用了拷贝构造函数,最好写成 c(a);
c = b; // 调用了赋值函数
本例中第三个语句的风格较差,宜改写成String c(a) 以区别于第四个语句。

如果我们实在不想编写拷贝构造函数和赋值函数,又不允许别人使用编译器生成的缺省函数,可以将拷贝构造函数和赋值函数声明为私有函数,不用编写代码。


3.析构函数与虚析构函数
基类的构造函数、析构函数、赋值函数都不能被派生类继承。如果类之间存在继承关系,在编写上述基本函数时应注意以下事项:
@ 派生类的构造函数应在其初始化表里调用基类的构造函数
@ 基类与派生类的析构函数应该为虚(即加virtual关键字)
#include
class Base
{
public:
virtual ~Base() { cout<< "~Base" << endl ; }
};
class Derived : public Base
{
public:
virtual ~Derived() { cout<< "~Derived" << endl ; }
};

void main(void)
{
Base * pB = new Derived; // upcast
delete pB;
}

输出结果为:
~Derived
~Base
如果析构函数不为虚,那么输出结果为
~Base

简介vc中的release和debug版本的区别

Debug通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。Release称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。

Debug 和 Release 的真正秘密,在于一组编译选项。下面列出了分别针对二者的选项(当然除此之外还有其他一些,如/Fd /Fo,但区别并不重要,通常他们也不会引起 Release 版错误,在此不讨论)

Debug 版本


参数 含义
/MDd /MLd 或 /MTd 使用 Debug runtime library (调试版本的运行时刻函数库)
/Od 关闭优化开关
/D "_DEBUG" 相当于 #define _DEBUG,打开编译调试代码开关 (主要针对assert函数)
/ZI 创建 Edit and continue(编辑继续)数据库,这样在调试过程中如果修改了源代码不需重新编译
/GZ 可以帮助捕获内存错误
/Gm 打开最小化重链接开关, 减少链接时间



Release 版本


参数 含义
/MD /ML 或 /MT 使用发布版本的运行时刻函数库
/O1 或 /O2 优化开关,使程序最小或最快
/D "NDEBUG" 关闭条件编译调试代码开关 (即不编译assert函数)
/GF 合并重复的字符串, 并将字符串常量放到只读内存, 防止被修改



实际上,Debug 和 Release 并没有本质的界限,他们只是一组编译选项的集合,编译器只是按照预定的选项行动。事实上,我们甚至可以修改这些选项,从而得到优化过的调试版本或是带跟踪语句的发布版本。

哪些情况下 Release 版会出错

有了上面的介绍,我们再来逐个对照这些选项看看 Release 版错误是怎样产生的

1、Runtime Library:链接哪种运行时刻函数库通常只对程序的性能产生影响。调试版本的 Runtime Library 包含了调试信息,并采用了一些保护机制以帮助发现错误,因此性能不如发布版本。编译器提供的 Runtime Library 通常很稳定,不会造成 Release 版错误;倒是由于 Debug 的 Runtime Library 加强了对错误的检测,如堆内存分配,有时会出现 Debug 有错但 Release 正常的现象。应当指出的是,如果 Debug 有错,即使 Release 正常,程序肯定是有 Bug 的,只不过可能是 Release 版的某次运行没有表现出来而已。

2、优化:这是造成错误的主要原因,因为关闭优化时源程序基本上是直接翻译的,而打开优化后编译器会作出一系列假设。这类错误主要有以下几种:

1. 帧指针(Frame Pointer)省略(简称FPO):在函数调用过程中,所有调用信息(返回地址、参数)以及自动变量都是放在栈中的。若函数的声明与实现不同(参数、返回值、调用方式),就会产生错误,但 Debug 方式下,栈的访问通过 EBP 寄存器保存的地址实现,如果没有发生数组越界之类的错误(或是越界“不多”),函数通常能正常执行;Release 方式下,优化会省略 EBP 栈基址指针,这样通过一个全局指针访问栈就会造成返回地址错误是程序崩溃。

C++ 的强类型特性能检查出大多数这样的错误,但如果用了强制类型转换,就不行了。你可以在 Release 版本中强制加入/Oy-编译选项来关掉帧指针省略,以确定是否此类错误。此类错误通常有:MFC 消息响应函数书写错误。正确的应为:


afx_msg LRESULT OnMessageOwn
(WPARAM wparam, LPARAM lparam);

ON_MESSAGE 宏包含强制类型转换。防止这种错误的方法之一是重定义 ON_MESSAGE 宏,把下列代码加到 stdafx.h 中(在#include "afxwin.h"之后),函数原形错误时编译会报错。


#undef ON_MESSAGE
#define ON_MESSAGE(message, memberFxn) \
{
message, 0, 0, 0, AfxSig_lwl, \
(AFX_PMSG)(AFX_PMSGW)
(static_cast< LRESULT (AFX_MSG_CALL \
CWnd::*)(WPARAM, LPARAM) > (&memberFxn)
},


2. volatile 型变量:volatile 告诉编译器该变量可能被程序之外的未知方式修改(如系统、其他进程和线程)。优化程序为了使程序性能提高,常把一些变量放在寄存器中(类似于 register 关键字),而其他进程只能对该变量所在的内存进行修改,而寄存器中的值没变。

如果你的程序是多线程的,或者你发现某个变量的值与预期的不符而你确信已正确的设置了,则很可能遇到这样的问题。这种错误有时会表现为程序在最快优化出错而最小优化正常。把你认为可疑的变量加上 volatile 试试。

3. 变量优化:优化程序会根据变量的使用情况优化变量。例如,函数中有一个未被使用的变量,在 Debug 版中它有可能掩盖一个数组越界,而在 Release 版中,这个变量很可能被优化调,此时数组越界会破坏栈中有用的数据。当然,实际的情况会比这复杂得多。与此有关的错误有非法访问,包括数组越界、指针错误等。例如:


void fn(void)
{
int i;
i = 1;
int a[4];
{
int j;
j = 1;
}
a[-1] = 1;
//当然错误不会这么明显,例如下标是变量
a[4] = 1;
}




j 虽然在数组越界时已出了作用域,但其空间并未收回,因而 i 和 j 就会掩盖越界。而 Release 版由于 i、j 并未其很大作用可能会被优化掉,从而使栈被破坏。

3. DEBUG 与 NDEBUG :当定义了 _DEBUG 时,assert() 函数会被编译,而 NDEBUG 时不被编译。此外,TRACE() 宏的编译也受 _DEBUG 控制。

所有这些断言都只在 Debug版中才被编译,而在 Release 版中被忽略。唯一的例外是 VERIFY()。事实上,这些宏都是调用了assert()函数,只不过附加了一些与库有关的调试代码。如果你在这些宏中加入了任何程序代码,而不只是布尔表达式(例如赋值、能改变变量值的函数调用等),那么Release版都不会执行这些操作,从而造成错误。初学者很容易犯这类错误,查找的方法也很简单,因为这些宏都已在上面列出,只要利用 VC++ 的 Find in Files 功能在工程所有文件中找到用这些宏的地方再一一检查即可。另外,有些高手可能还会加入 #ifdef _DEBUG 之类的条件编译,也要注意一下。

顺便值得一提的是VERIFY()宏,这个宏允许你将程序代码放在布尔表达式里。这个宏通常用来检查 Windows API的返回值。有些人可能为这个原因而滥用VERIFY(),事实上这是危险的,因为VERIFY()违反了断言的思想,不能使程序代码和调试代码完全分离,最终可能会带来很多麻烦。因此,专家们建议尽量少用这个宏。

4. /GZ 选项:这个选项会做以下这些事:

1. 初始化内存和变量。包括用 0xCC 初始化所有自动变量,0xCD ( Cleared Data ) 初始化堆中分配的内存(即动态分配的内存,例如 new ),0xDD ( Dead Data ) 填充已被释放的堆内存(例如 delete ),0xFD( deFencde Data ) 初始化受保护的内存(debug 版在动态分配内存的前后加入保护内存以防止越界访问),其中括号中的词是微软建议的助记词。这样做的好处是这些值都很大,作为指针是不可能的(而且 32 位系统中指针很少是奇数值,在有些系统中奇数的指针会产生运行时错误),作为数值也很少遇到,而且这些值也很容易辨认,因此这很有利于在 Debug 版中发现 Release 版才会遇到的错误。要特别注意的是,很多人认为编译器会用0来初始化变量,这是错误的(而且这样很不利于查找错误)。

2. 通过函数指针调用函数时,会通过检查栈指针验证函数调用的匹配性。(防止原形不匹配)

3. 函数返回前检查栈指针,确认未被修改。(防止越界访问和原形不匹配,与第二项合在一起可大致模拟帧指针省略 FPO )通常 /GZ 选项会造成 Debug 版出错而 Release 版正常的现象,因为 Release 版中未初始化的变量是随机的,这有可能使指针指向一个有效地址而掩盖了非法访问。除此之外,/Gm/GF等选项造成错误的情况比较少,而且他们的效果显而易见,比较容易发现。

怎样“调试” Release 版的程序

遇到Debug成功但Release失败,显然是一件很沮丧的事,而且往往无从下手。如果你看了以上的分析,结合错误的具体表现,很快找出了错误,固然很好。但如果一时找不出,以下给出了一些在这种情况下的策略。

1. 前面已经提过,Debug和Release只是一组编译选项的差别,实际上并没有什么定义能区分二者。我们可以修改Release版的编译选项来缩小错误范围。如上所述,可以把Release 的选项逐个改为与之相对的Debug选项,如/MD改为/MDd、/O1改为/Od,或运行时间优化改为程序大小优化。注意,一次只改一个选项,看改哪个选项时错误消失,再对应该选项相关的错误,针对性地查找。这些选项在Project\Settings...中都可以直接通过列表选取,通常不要手动修改。由于以上的分析已相当全面,这个方法是最有效的。

2. 在编程过程中就要时常注意测试 Release 版本,以免最后代码太多,时间又很紧。

3. 在 Debug 版中使用 /W4 警告级别,这样可以从编译器获得最大限度的错误信息,比如 if( i =0 )就会引起 /W4 警告。不要忽略这些警告,通常这是你程序中的 Bug 引起的。但有时 /W4 会带来很多冗余信息,如 未使用的函数参数 警告,而很多消息处理函数都会忽略某些参数。我们可以用:


#progma warning(disable: 4702)
//禁止
//...
#progma warning(default: 4702)
//重新允许来暂时禁止某个警告,或使用
#progma warning(push, 3)
//设置警告级别为 /W3
//...
#progma warning(pop)
//重设为 /W4




来暂时改变警告级别,有时你可以只在认为可疑的那一部分代码使用 /W4。

4. 你也可以像Debug一样调试你的Release版,只要加入调试符号。在Project/Settings... 中,选中 Settings for "Win32 Release",选中 C/C++ 标签,Category 选 General,Debug Info 选 Program Database。再在 Link 标签 Project options 最后加上 "/OPT:REF" (引号不要输)。这样调试器就能使用 pdb 文件中的调试符号。

但调试时你会发现断点很难设置,变量也很难找到??这些都被优化过了。不过令人庆幸的是,Call Stack窗口仍然工作正常,即使帧指针被优化,栈信息(特别是返回地址)仍然能找到。这对定位错误很有帮助。

2009年5月4日星期一

Configure Codeblocks For Qt on Windows

将安装目录下\MinGW\bin内的mingw32-make文件复制并改名为make。

1.在Code::Blocks里选择Tools —> Configure tools... 添加两个新的工具
A.第一个工具,在弹出的工具编辑框中依次填入
Name: create Qt.pro file
Executable: c:\Qt\4.4.3\bin\qmake.exe (即本机上qmake的绝对路径)
Parameters: -project
Working directory: ${PROJECT_DIR}
下面的Launching options选择 Launch tool hidden with standard output redirected (这样就不会跳出命令行窗口)

B.第二个工具,在弹出的工具编辑框中依次填入
Name: create Qt makefile
Executable: c:\Qt\4.4.3\bin\qmake.exe (即本机上qmake的绝对路径)
Parameters: -makefile
Working directory: ${PROJECT_DIR}
下面的Launching options选择 Launch tool hidden with standard output redirected (这样就不会跳出命令行窗口)

4.选择Project —> Properties... —> Project settings,在"This is a custom Makefile"前打勾,这样就会启用Makefile来编译
5.转到Build targets,将output filename由bin\Debug\xxx.exe改成Debug\xxx.exe,即去掉前面的bin\
6.选择Project —> Build options... —> "Make" commands,修改两个地方
Clean project/target: $make -f $makefile $target-clean
Dist-clean project/target: $make -f $makefile $target-distclean

所有地方都设置好了,接下来只要在Build前,先在Tools下依次运行create Qt.pro file和create Qt makefile (只需在项目有文件添加和删除的情况下需要运行一遍)

2009年4月28日星期二

QT4.4.3+windows vs2005编译安装详解

一. 安装准备:

1. 准备安装文件:

(1):Qt 4.4.3 OpenSource for Windows

下载地址: ftp://ftp.trolltech.no/qt/source/qt-win-opensource-src-4.4.3.zip

(2)Qt Visual Studio Integration v1.2.2 for.VS.2003.2005 英文版

下载地址:http://www.fixdown.com/china/Programming/2525.htm

(3)Visual Assist (代码提示功能,选装)

2. 已安装VS2005,安装其中C++即可.

3. 将下载的Qt 4.4.3文件解压. 如解压至目录D:\QT\4.4.3

4. 设置环境变量

右击我的电脑->属性->高级->环境变量;

在系统变量中新建QTDIR变量,变量值D:\QT\4.4.3

新建QMAKESPEC变量,变量值win32-msvc2005

在PATH变量中填加:D:\QT\4.4.3\bin;

PS:

==============================

value platform

win32-msvc VC6
win32-msvc2003 VS2003
win32-msvc2005 VS2005
win32-msvc2008 VS2008

==============================

至此准备工作结束.



二. 编译QT

1. 配置

在命令行提示符下进入QTDIR目录,如cd D:\QT\4.4.3

在命令行中输入configure -no-dsp -vcproj

(具体参数设置可输入configure /?查看)

configure 大约需几分钟时间.

2. 编译

configure完成后,会有提示.此时只需输入nmake进行编译.编译所花时间较长,两三个小时(视机器性能而定).



三. 安装Qt Visual Studio Integration v1.2.2

直接安装即可,用户名任意,注册码可取下列值之一:

FG6ALD-Y2PYGSX-F4M-LQAHD59-JZ6QKN-FJ7PXV-049B

FGEZDGX-AZPBJ8X-F4M-K44SA7M-UEG7TH-UYAML8M-05D7

FGPDXA9-MXKFJYM-F4M-BHEDKZX-8XCETXX-92WFVWX-A05E

FGWNPUX-Y6X26L9-F4M-GLZMY99-97RXPJX-BP256DX-737C



四. 配置VS2005

"工具"->"选项"->"QT"->"Builds"添加version name: 4.4.3 PATH: D:\QT\4.4.3

若安装了Visual Assist,则需选择"VAssistX" -> "Visual Assist X Options ..." -> "Projects", 在"Platform"里面把"Win32"改为"Custom",
在"Stable include files"中添加D:\Qt\4.3.3\include目录下的子文件夹.



将D:\QT\4.4.3\bin目录下的DLL文件拷到C:\windows目录下(注意,重装QT时记得删除或替换.否则可能会出现找不到链接入口的错误.)

至此安装结束.



五. 测试

VS2005中:"文件"->"新建"->"项目",选择"Qt Projects"->"Qt Application".输入名称.确定,FINISH,OK

双击解决方案中"hello.ui",即可弹出窗体编辑器.在窗体上加入一个WIDGET,如BUTTON.



编译运行即可.

2009年4月26日星期日

Qt 4.5 with Visual Studio 2008 (VC++ Express)

Qt is a cross platform GUI toolkit from Nokia. Its SDK is available under an LPGL license, it means we can use it free of charge to develop proprietary, commercial, closed-source software (thanks to Nokia). However, the current LPGL release leaves some gaps in using Qt from within Visual Studio 2008. This is somewhat understandable given that Visual Studio integration was a commercial-only offering and Nokia has Qt Creator (and not Visual Studio). My problem with Qt Creator is it uses MingGW (gcc) compiler which is nowhere near as good as VC++ 2008. Also for Visual Studio background like me, who is not familiar with Qt creator, learning Qt Creator will take time. Thanks to Wiria and AK who brought Qt to my table for a review.


Qt GUI Toolkit is very useful to develop portable application (single source codes) for *NIX, MAC and Windows (including Windows 98 but not being tested for Windows 7). In this post, I will show you how to start using Qt SDK only with Visual C++ 2008, you can use FREE Visual C++ 2008 express too.


Build VC++ Version of Qt


- Download and install qt-sdk-win-opensource-2009.01.1.
- Install the SDK to default directory, in my case C:\qt\2009.01. I didn;t install MingGW and Qt Creator as I don’t need.


- Once QT and VisualStudio 2008 both are installed and ready, open the VisualStudio 2008 Command Prompt.
- Change directory to the Qt SDK installation folder in the Command Prompt. Usually it would be of the form "C:\Qt\2009.01\qt"
- Run Configure.exe to target platform win32-msvc2008. You can simply run by typing configure or using other options. For example:

C:\Qt\2009.01\configure -no-sql-sqlite -no-qt3support -no-opengl -platform win32-msvc2008
-no-libtiff -no-dbus -no-phonon -no-phonon-backend -no-webkit

- Build Qt for use with Visual Studio 2008 by typing nmake
- Configure and nmake will take time, be patient enough. In my case, I started to build 19.17 PM and completed on 23.47 (4:30 hours)!
- Once completed, Qt SDK is ready for Visual Studio 2008.



Build and Run the Qt SDK Samples

Once your Qt SDK is ready to be used you can see samples directory in your Qt SDK installation (usually, C:\Qt\2009.01\qt\examples) and then open the examples.sln file from your Visual Studio 2008. If double-clicking .sln file does not work, open it manually from your Visual Studio. There are a lot of samples from Qt SDK.



If you build and debug – calculator project of instance – you will notification that the application because QtGuid4.dll was not found.



To let Visual Studio knows the include and library files of Qt, add their paths to the:

Visual Studio Tools|Options|Projects and Solutions|VC++ Directories

section. Typically you would want to add C:\Qt\2009.01\qt\include to the Include, C:\Qt\2009.01\qt\lib to the libraries and C:\Qt\2009.01\qt\bin to the Executables sections. Also you can add C:\Qt\2009.01\qt\bin to the Path System variable so that the Qt dlls get probed correctly when loaded from your application.



A lot of Qt samples inside the SDK. Try to build and run those sample applications to see how powerful Qt as GUI Toolkit.




Using Qt’s Visual Studio 2008 Plug-in

You can try Visual Studio add-in RC version (this will be a commercial product) to get integrated Qt templates and visual IDE features in Visual Studio 2008. I will not cover in this post, but this tools is promising. I will show you in my other Qt-VC++ post.



Hope this helps!

2009年4月15日星期三

www.sokmil.com/

无法查看这则摘要。请 点击此处查看博文。

multiple types in one declaration

multiple types in one declaration是什么意思?
悬赏分:5 - 解决时间:2008-6-17 14:44
在编译c++时出现这个问题,请大家帮助!
提问者: hexik - 秀才 三级
最佳答案
类型定义的时候忘了加分号了吧?
回答者: tanarri - 大魔法师 九级 6-17 14:41
我来评论>>
提问者对于答案的评价:
老兄你真行啊!我刚在csdn也找到答案了,早一点发上来找你就不会那么辛苦了,谢谢!

windows下的系统变量

在设置系统环境变量的过程中,经常会看到诸如%SystemRoot% ,%windir% ,%temp% 的字眼,当初我也不知道指的是那些目录,经过一段时间的探索,终于还是弄懂了,总结一下分享给大家:对于操作系统是XP(系统目录是windows)并安装在C盘的用户icesoul
格式:变量名=实际含义
---------------------------------------------------------------
%HOMEDRIVE% = C:\ 当前启动的系统的所在分区
%SystemRoot% = C:\WINDOWS 当前启动的系统的所在目录
%windir% = %SystemRoot% = C:\WINDOWS 当前启动的系统的所在目录
%USERPROFILE% = C:\Documents and Settings\icesoul 当前用户数据变量
%HOMEPATH% = C:\Documents and Settings\icesoul 当前用户环境变量
%temp% = C:\Documents and Settings\icesoul\Local Settings\Temp 当前用户TEMP缓存变量
%programfiles% = C:\Program Files 程序安装目录
%commonprogramfiles% = G:\Program Files\Common Files 通用文件目录
%system% = C:\WINDOWS\SYSTEM32 系统安装盘下的system32目录
%System%在98下指windows\system,2000下指winnt\system32\,XP下指windows\system32文件夹
---------------------------------------------------------------

声明:(WINDOWS XP,2K3)下系统目录为“WINDOWS”(WINDOWS 2K,98,ME)下系统目录为“WINNT”

例如:%windir%\drives 的实际路径就是 C:\WINDOWS\drives 目录。

所谓变量,就是指一个在不同环境中会有相对不同的值的、但在所有环境中都有相同约定的含义的量。

这些变量,可以在开始菜单-运行中输入,如输入%SystemRoot% ,系统会直接打开 C:\WINDOWS 目录。

如果你当前的系统是装在D盘的2000的话,上边这个变量的执行结果就是打开 D:\WINNT 目录了。

创业中的人

创业不是用钱就能堆得出来的,创业是人在创造伟大的事业,“人”是创业中最关键、最重要的组成部分。
大公司招人容易,可以在报纸上整版整版刊登招聘广告,自我吹嘘什么世界500强啊,什么福利好哇,什么培训计划、职业生涯呀,天花乱坠说得那些没见过世面的小弟弟小妹妹们人人心里痒痒的,像小狗崽儿们见到了屎…不不不,对不起说错了,像蜜蜂见到了鲜花,恨不得钻进那里面去一辈子干到老。

创业公司没有钱,要门面没门面,要排场没排场,如何才能找到好人、高人?而大多数前来找工作的人除了在乎公司支付的工资福利以外,最看重的是“安全感”,创业公司都是汪洋中的小舢板,毫无“安全感”可言;创业公司也不像大公司,没法用“品牌”、“资本”来支撑人们的信心,而摆在创业者面前痛苦的现实是:创业公司必须要有精兵强将才能去挑战大公司,去颠覆大公司,去抢占大公司所意识不到的市场机遇……

识人和用人

创业需要精兵强将,需要搜罗天下高人,最简单的方法是在网上找,你把年龄、学历、工作经历往招聘网站上一输入,电脑会自动为你配送所需要的人才,即所谓网络“速配”,在这里,“人”和你去商场里买的“货”差不多,你提出硬性指标,机器马上给你提供“速配”的结果。仔细想一想看吧,这种“速配”方法也许给猪配种可以,给创业公司配人,能行么?!

创业公司里的精兵强将是很难完全按学历、工作经历来挑选的。一个名牌大学的电脑博士,又在跨国大公司里混到了副总裁的职位,此人是不是有能力、有兴趣到你寒碜的创业公司里任高管,带领一家小公司从无到有,从小到大,从亏损到盈利,天晓得。

大公司里的人才和小公司里需要的人才是不一样的,无论他们的能力,价值观,做事风格、奋斗目标也都大不一样:

大公司里人浮于事 创业公司里没有肥肉
大公司里得能说会道 创业公司里得少说多做
大公司里层层官僚 创业公司里说干就干
大公司公务舱五星级宾馆 创业公司经济舱+如家经济房
大公司每天8小时 创业公司每天16小时
大公司一人干一件事情 创业公司一人干十件事情
大公司里阿姨扫地 创业公司里老板扫地
大公司里人人都是螺丝钉 创业公司里人人都是一条龙
……

让精兵强将们放弃大公司里舒舒服服的职位、薪资、福利,以及按步就班的升迁机会去跟你上梁山、上井冈山,夜以继日、年复一年地艰苦奋斗闹革命,凭什么啊?还不是因为你,创业者你的伟大愿景、你的崇高信念、你的个人魅力感动了他们!人们从你身上看到了比大公司更美妙的远景和机缘,于是他们“投资”了你,他们把自己的光辉前程压宝压在了你身上,前来投奔你跟你奋斗。

精兵强将们不会逆来顺受,也不是那种你发号施令他就立正转身起步向前走的人,精兵强将意味着比你强的人、和你观点不一致的人、难以驾驭的人、甚至是你不喜欢的人。

搜罗和降伏精兵强将需要有宽广的胸怀、耐性、宽容、智慧。创业者要能够从与你不同的观点、性格、能力、风格中,透视出谁是你能力的补充、提高和延伸?

创业者要能够给新人以机会:如果有人本来就是个大公司的副总,再来你的小公司里当副总,未免会有大材小用的感觉;如果有人本来只是个大公司的部门经理,到了你的小公司被提拔成了副总,他肯定会想努力搞出些名堂来大显一番身手;

创业者要有耐心:本来学电影编导的,现在满腔热情来做软件编程,不管他是多么的勤奋好学天资多高,总得给他些时间、容忍几次失败吧?本来只是个部门经理,现在当上了公司COO,第一次编写公司的运营手册,总得耐心让他去学习酝酿、反复修改;

创业者要严格把关:人招进来了并不等于招聘大功告成了,你得在试用期里仔细观察确定,此人到底是不是完全合乎你公司的需求和要求。说句难听话,世界是个大舞台,人们涂脂抹粉乔装打扮、忙忙碌碌各显神通地在各个角落里忽悠别人的钱,要在茫茫人海里找到个真正有能力、可以信赖的好人并不是一件轻而易举的容易事情。

激人和留人

曾经有个小厮大言道:A类人才找A+++类人才,B类人才找C类人才,C类人才找N类人才,N 类人才找Z类人才……

此小厮勉强算得上是个A类人才,A类的人才找A+++类人才,小厮也带了个头,虽然他自己有点像是个山寨机公司的创始人,但小厮他竟然斗胆爬到了百事可乐的镇山之王斯考利的肩膀上,踮着脚尖对准大王的耳朵大声说:“你愿意老死买糖水,还是原意跟我去改变世界?!”小厮的这句话,如醍醐灌顶让堂堂不可一世的百事可乐的全球CEO斯考利楞住了,一句话使得斯考利夜不能寐,第二天一早决定舍弃江山,投奔曹营。

小厮就是乔布斯,Apple的CEO就是这么被小乔给忽悠来的。

“人”是财富,是创业公司里最大的投资,创业公司招人和VC投资一样,有三部曲,第一步是要招进高人来(等于VC找好项目);第二步是要试人驯人,看看是不是货真价实,能不能得心应手(等于VC的尽职调查);第三步是留人走人,好人留下,不合格的走人(好的项目VC投钱,不灵的项目VC说拜拜)。

创业公司里尤其需要团队精神和凝聚力,创业公司招人不妨让它变成一件团队的事情,发动团队来挖人,每个人都分配到一定份额,要推荐进来比自己强很多的人,要他们去把以前公司里的优秀上司都挖进来,面试的过程也可以全员参与,当人人都觉得应聘者出色,而不只是老板自己看中意的人,才能被招进来。Google 最初招聘任何一个人,都必须全体员工面试,一定要获得全票赞同才能进门,所以Google初期找进来的人过五关斩六将自己都是精兵强将,听说Google 现在还坚持这个传统,不知道这种小公司的方法是不是还可以适应大公司的环境,要是新招聘一个人需要经过几百几千人的一致同意,那进来的人必须十全十美的、没有缺陷的、没有棱角的、因此也没有性格的啦。

创业公司招到了高人,要大胆地提拔使用他们,也要慷慨地激励他们,并且牢牢地留住他们,高人当然都是值钱的,但是创业公司银行账号里能够取出来收买人心的银子太少,只能画饼充饥,把公司的股份拿出来慰劳一起打天下的弟兄们,创业公司的股票犹如一张张美丽的图画,对于“信徒”、对于相信的人来说,它就是钱,而且是大钱,在不相信的人眼里,就是一文废纸。

创业公司里的股份和期权意味着公司的未来,创业者弟兄们想好了再动手,可别大意啊,尤其在你确定公司最初的股权、期权和价值的时候,如果犯了错误,日后是很难改正过来的。

股份

创业公司的股份通常是在创业最初就必须确定下来的公司权益。

最原始的股份分配方法就是按出资比例确定,你钱多能多出钱,你占股份的比例就大,我钱少出不起钱,我的股份就少甚至没有。这种以出钱比例决定股权比例的方法在VC的眼睛里是不合理的(哈哈,终于找到一件要大谢特谢VC的名堂了)。因为从VC的角度看,一个创业公司的价值,不是投进去的钱的多少,而是创业者的努力,创业者使得公司的价值不断放大,因此创业者投入很少的钱甚至不投钱,也值得让他们拿公司股权的大头,VC投进创业公司里一大笔钱,通常只会去占公司的一小部分股份,这样可以让团队看到了自己的价值,让他们会有足够的动力去拼命为创业公司的业绩增长而苦战。

举个列子说明一下,如果两个创业者各出10万元钱各占10%公司股份,而投资人投入的80万占了公司80%的股权,那么这家公司的总价值是100万,全是金钱票面的价值,“人”在这里面体现不出任何“价值”。

如果还是这两个创业者,VC出了1000万拿了20%的股份,而两个创业者没有投入实际的资金,他们投入的是Idea,加他们的全部时间和精力,在这个例子里,两位创业者各自的身价已经达到了2000万,和上面一个例子比较一下就不难看出哪一个情形中的创业者会为公司卖命更加爽气。

当然VC出了1000万只拿了20%公司股份是有一定计算和认价方法的,但这里更重要的是VC对创业者的“信念”,从某种角度说,创业是一种信念、创投也是一种信念。

当然,在VC还没有进来之前,原始的创业股东们确定自己的股份,一方面可以由出资多少决定股份的多少,另一方面也应考虑每个人的能力、作用来相应决定所持股权的比例,创业公司的股权大可不必搞平均主义,千万不要弟兄几个相互客气相互谦让,要坚持一个公司里必须有一个核心的人物,坚持贡献多的人就应该理所当然多拿些股份,贡献少能力差些的,就相应少拿些。比如说可以把50%的公司股份按照出资比例来确定,另外50%按照创始人的能力贡献差异性地分配。创业公司的股权要尽可能合理化地分配,这样才能保持团队长期合作的公平性。创业者们应该开诚布公从第一天就合理分配公司股权,关键的问题大家先说好,不留任何后患。

期权

创业公司里除了原始的创业团队以外,还会不断进来新人、新的精兵强将。公司的股份是吸引、激励、留住精兵强将的最好手段。给后来团队的股份,严格地说叫“期权”。也就是说这些股权需要经过一段时间才能成熟,比如3年。

举例来说,创业者从大公司里挖来了一个CTO,同意给他10%的股份并和他签署了他持有10%公司期权的文件。如果这个CTO人尽其能非常出色,到了第三年底,3年期权成熟,他就实际拥有了这家公司10%的股份。如果因为某些原因,这位CTO在创业公司里呆了一年就决定离开,那么他的10%的股权只成熟了 1/3,他离职的那天应该把他的所持股份做一清算,他该拿到的1/3,即3.3%的股份就应让他正式合理持有,剩下没有成熟的2/3,即6.6%应该返还给公司。返还的6.6%的公司股份是留在公司里的而不是又落入了谁的腰包,当进来了一位新的接替的CTO时,这6.6%的股份可以用作第二位CTO的期权给他(她)。

创业公司的股份和期权是动态的,一方面,随着每一次的增发(比如进来了新的投资人),每个原始股东的股份百分比都会出现相应的稀释,另一方面,每一个VC都会要求在新一轮VC投资进来时,再重新划出一部分公司股权来留作为吸引更多精兵强将加盟的“期权池”。

创业公司的股权在最初的时候可以用百分比来算,但是随着公司股本数的扩大,公司价值的增加,公司中持股人数的增多,以公司股票的绝对值而不是百分比来计算期权可能会更加合理。比如来了一个市场总监,公司决定授予价值100万元的期权,公司这时有可能已经发了上亿的股票,所以这价值100万的期权加在一起可能连1%的公司股份都不到,但是按照公司每一股的实际价值计算,这不到1%的股份的价值可能已经达到甚至超过了100万元钱。

融资也是一门艺术,创业者们在融资的时候不应该胃口太大心太黑,一下子融太多钱。按一定周期融钱,比如每12-18个月融一次,而且每一轮融资都做到成倍的溢价,每一次溢价都是新投资人对你公司的价值认可,真金白银,货真价实;在不断的溢价过程中,团队们看到了公司在成长,自己手里的股票也在增值,因此他们对你创业者的信心和崇敬之情也会不断提升;公司里老员工手里的股票的增值幅度一定比新来的员工更多,股权和期权像一座金字塔,起到了凝聚团队和稳住留住好人的作用。

斩人

创业公司像是淘金用的箩筐,大浪淘沙,把优秀的人才留下来,让他们闪闪发光,让经受不了考验的沙子冲走。炒人鱿鱼会伤人感情,产生后遗症,《资治通鉴》里也没有详细的方法指点,所以冒昧提几条建议仅供参考:

1. 充分考虑,绝不后悔。炒人鱿鱼,人家会有心灵创伤,所以即使被炒者是你原来两小无猜的朋友,你大义灭亲,果断处置,人家不一定会对你有理解之心,大家共事不成,朋友恐怕也就此了结。

2. 充分准备,后路铺好。炒人鱿鱼,此人的案头工作立刻要有人跟进,比如客户的衔接,马上派一名更加有经验的同事去打理。

3. 快刀斩乱麻,干净利落,当天通知,当天走人,不要给予诸如15天的提前通知之类,让被炒的人继续在办公室里待下去,人家知道自己已经被炒鱿鱼,心已飞走,没有谁还会安心在公司里继续卖命苦干。

4. 平静处置,以人为本。炒人要有充足理由,使人心服口服;让被炒者有发言的权利,让人有机会发泄心理不平,但是势态一定要平静,局面要控制住;对被炒者仁慈一些,盘缠给足,多给一个月工资也就是一个月工资,想想此人如果留在公司不出力,工资还得一个月一个月不停地发;公司炒人,人心惶惶,要做好安抚工作,让其它人觉得即使下回炒鱿鱼轮到了他们自己,你的大度和公正,大家都会觉得问心无愧。

5. 创业公司要时刻保持消瘦身材,切忌肥胖,即使没有人的业绩表现异常出格,公司也应该定期做业绩评定,末位淘汰。这样的话,本来三个人的工作现在两个人做,本来三个人的工资也可以发给两个人,创业公司的优越性也发挥了出来,员工的责任更大,个人收入也更高,一点不比大公司差。

2009年4月9日星期四

"MySQL"索引的分析和优化

一、什么是索引?


索引用来快速地寻找那些具有特定值的记录,所有MySQL索 引都以B-树的形式保存。如果没有索引,执行查询时MySQL必须从第一个记录开始扫描整个表的所有记录,直至找到符合要求的记录。表里面的记录数量越 多,这个操作的代价就越高。如果作为搜索条件的列上已经创建了索引,MySQL无需扫描任何记录即可迅速得到目标记录所在的位置。如果表有1000个记 录,通过索引查找记录至少要比顺序扫描记录快100倍。


假设我们创建了一个名为people的表:


CREATE TABLE people ( peopleid SMALLINT NOT NULL, name CHAR(50) NOT NULL );






然 后,我们完全随机把1000个不同name值插入到people表。在数据文件中name 列没有任何明确的次序。如果我们创建了name列的索引,MySQL将在索引中排序name列,对于索引中的每一项,MySQL在内部为它保存一个数据文 件中实际记录所在位置的“指针”。因此,如果我们要查找name等于“Mike”记录的peopleid(SQL命令为“SELECT peopleid FROM people WHERE name='Mike'; ”),MySQL能够在name的索引中查找“Mike”值,然后直接转到数据文件中相应的行,准确地返回该行的 peopleid(999)。在这个过程中,MySQL只需处理一个行就可以返回结果。如果没有“name”列的索引,MySQL要扫描数据文件中的所有 记录,即1000个记录!显然,需要MySQL处理的记录数量越少,则它完成任务的速度就越快。


二、索引的类型


MySQL提供多种索引类型供选择:


普通索引 :


这是最基本的索引类型,而且它没有唯一性之类的限制。普通索引可以通过以下几种方式创建:


创建索引,例如CREATE INDEX <索引的名字> ON tablename (列的列表);


修改表,例如ALTER TABLE tablename ADD INDEX [索引的名字] (列的列表);


创建表的时候指定索引,例如CREATE TABLE tablename ( [...], INDEX [索引的名字] (列的列表) );


唯一性索引:


这种索引和前面的“普通索引”基本相同,但有一个区别:索引列的所有值都只能出现一次,即必须唯一。唯一性索引可以用以下几种方式创建:


创建索引,例如CREATE UNIQUE INDEX <索引的名字> ON tablename (列的列表);


修改表,例如ALTER TABLE tablename ADD UNIQUE [索引的名字] (列的列表);


创建表的时候指定索引,例如CREATE TABLE tablename ( [...], UNIQUE [索引的名字] (列的列表) );


主键 :


主 键是一种唯一性索引,但它必须指定为“PRIMARY KEY”。如果你曾经用过AUTO_INCREMENT类型的列,你可能已经熟悉主键之类的概念了。主键一般在创建表的时候指定,例如“CREATE TABLE tablename ( [...], PRIMARY KEY (列的列表) ); ”。但是,我们也可以通过修改表的方式加入主键,例如“ALTER TABLE tablename ADD PRIMARY KEY (列的列表); ”。每个表只能有一个主键。





全文索引:


MySQL从3.23.23版开 始支持全文索引和全文检索。在MySQL中,全文索引的索引 类型为FULLTEXT。全文索引可以在VARCHAR或者TEXT类型的列上创建。它可以通过CREATE TABLE命令创建,也可以通过ALTER TABLE或CREATE INDEX命令创建。对于大规模的数据集,通过ALTER TABLE(或者CREATE INDEX)命令创建全文索引要比把记录插入带有全文索引的空表更快。本文下面的讨论不再涉及全文索引,要了解更多信息,请参见MySQL documentation。


三、单列索引与多列索引


索引可以是单列索引,也可以是多列索引。下面我们通过具体的例子来说明这两种索引的区别。假设有这样一个people表:


CREATE TABLE people ( peopleid SMALLINT NOT NULL AUTO_INCREMENT,
firstname CHAR(50) NOT NULL, lastname CHAR(50) NOT NULL,
age SMALLINT NOT NULL,townid SMALLINT NOT NULL, PRIMARY KEY (peopleid) );






下面是我们插入到这个people表的数据:


这个数据片段中有四个名字为“Mikes”的人(其中两个姓Sullivans,两个姓McConnells),有两个年龄为17岁的人,还有一个名字与众不同的Joe Smith。


这 个表的主要用途是根据指定的用户姓、名以及年龄返回相应的peopleid。例如,我们可 能需要查找姓名为Mike Sullivan、年龄17岁用户的peopleid(SQL命令为SELECT peopleid FROM people WHERE firstname='Mike' AND lastname='Sullivan' AND age=17;)。由于我们不想让MySQL每次执行查询就去扫描整个表,这里需要考虑运用索引。


首先,我们可以考虑在单个 列上创建索引,比如firstname、lastname或者 age列。如果我们创建firstname列的索引(ALTER TABLE people ADD INDEX firstname (firstname);),MySQL将通过这个索引迅速把搜索范围限制到那些firstname='Mike'的记录,然后再在这个“中间结果集”上 进行其他条件的搜索:它首先排除那些lastname不等于“Sullivan”的记录,然后排除那些age不等于17的记录。当记录满足所有搜索条件之 后,MySQL就返回最终的搜索结果。


由于建立了firstname列的索引,与执行表的完全扫描相比,MySQL的效率提 高了很 多,但我们要求MySQL扫描的记录数量仍旧远远超过了实际所需要的。虽然我们可以删除firstname列上的索引,再创建lastname或者age 列的索引,但总地看来,不论在哪个列上创建索引搜索效率仍旧相似。


为了提高搜索效率,我们需要考虑运用多列索引。如果为firstname、lastname和age这三个列创建一个多列索引,MySQL只需一次检索就能够找出正确的结果!下面是创建这个多列索引的SQL命令:


ALTER TABLE people ADD INDEX fname_lname_age (firstname,lastname,age);






由于索引文件以B-树格式保存,MySQL能够立即转到合适的firstname,然后再转到合适的lastname,最后转到合适的age。在没有扫描数据文件任何一个记录的情况下,MySQL就正确地找出了搜索的目标记录!


那 么,如果在firstname、lastname、age这三个列上分别创建单列索引,效 果是否和创建一个firstname、lastname、age的多列索引一样呢?答案是否定的,两者完全不同。当我们执行查询的时候,MySQL只能使 用一个索引。如果你有三个单列的索引,MySQL会试图选择一个限制最严格的索引。但是,即使是限制最严格的单列索引,它的限制能力也肯定远远低于 firstname、lastname、age这三个列上的多列索引。


四、最左前缀


多列索引还有 另外一个优点,它通过称为最左前缀(Leftmost Prefixing)的概念体现出来。继续考虑前面的例子,现在我们有一个firstname、lastname、age列上的多列索引,我们称这个索引 为fname_lname_age。当搜索条件是以下各种列的组合时,MySQL将使用fname_lname_age索引:


firstname,lastname,agefirstname,lastnamefirstname






从另一方面理解,它相当于我们创建了(firstname,lastname,age)、(firstname,lastname)以及(firstname)这些列组合上的索引。下面这些查询都能够使用这个fname_lname_age索引:


SELECT peopleid FROM people
WHERE firstname='Mike' AND lastname='Sullivan' AND age='17';
SELECT peopleid FROM people WHERE firstname='Mike' AND lastname='Sullivan';
SELECT peopleid FROM people WHERE firstname='Mike';
The following queries cannot use the index at all:
SELECT peopleid FROM people WHERE lastname='Sullivan';
SELECT peopleid FROM people WHERE age='17';
SELECT peopleid FROM people WHERE lastname='Sullivan' AND age='17';


五、选择索引列


在性能优化过程中,选择在哪些列上创建索引是最重要的步骤之一。可以考虑使用索引的主要有两种类型的列:在WHERE子句中出现的列,在join子句中出现的列。请看下面这个查询:


SELECT age ## 不使用索引
FROM people WHERE firstname='Mike' ## 考虑使用索引
AND lastname='Sullivan' ## 考虑使用索引






这个查询与前面的查询略有不同,但仍属于简单查询。由于age是在SELECT部分被引用,MySQL不会用它来限制列选择操作。因此,对于这个查询来说,创建age列的索引没有什么必要。下面是一个更复杂的例子:


SELECT people.age, ##不使用索引
town.name ##不使用索引
FROM people LEFT JOIN town ON
people.townid=town.townid ##考虑使用索引
WHERE firstname='Mike' ##考虑使用索引
AND lastname='Sullivan' ##考虑使用索引


与 前面的例子一样,由于firstname和lastname出现在WHERE子句中,因此 这两个列仍旧有创建索引的必要。除此之外,由于town表的townid列出现在join子句中,因此我们需要考虑创建该列的索引。那么,我们是否可以简 单地认为应该索引WHERE子句和join子句中出现的每一个列呢?差不多如此,但并不完全。我们还必须考虑到对列进行比较的操作符类型。MySQL只有 对以下操作符才使用索引:<,<=,=,>,>=,BETWEEN,IN,以及某些时候的LIKE。可以在LIKE操作中使用索 引的情形是指另一个操作数不是以通配符(%或者_)开头的情形。例如,“SELECT peopleid FROM people WHERE firstname LIKE 'Mich%'; ”这个查询将使用索引,但“SELECT peopleid FROM people WHERE firstname LIKE '%ike'; ”这个查询不会使用索引。

mysql limit 查询优化

MYSQL的优化是非常重要的。其他最常用也最需要优化的就是limit。mysql的limit给分页带来了极大的方便,但数据量一大的时候,limit的性能就急剧下降。

同样是取10条数据
select * from yanxue8_visit limit 10000,10 和
select * from yanxue8_visit limit 0,10

就不是一个数量级别的。

网上也很多关于limit的五条优化准则,都是翻译自mysql手册,虽然正确但不实用。今天发现一篇文章写了些关于limit优化的,很不错。

文中不是直接使用limit,而是首先获取到offset的id然后直接使用limit size来获取数据。根据他的数据,明显要好于直接使用limit。这里我具体使用数据分两种情况进行测试。(测试环境win2033+p4双核 (3GHZ) +4G内存 mysql 5.0.19)

1、offset比较小的时候。

select * from yanxue8_visit limit 10,10

多次运行,时间保持在0.0004-0.0005之间
Select * From yanxue8_visit Where vid >=(
Select vid From yanxue8_visit Order By vid limit 10,1
) limit 10

多次运行,时间保持在0.0005-0.0006之间,主要是0.0006
结论:偏移offset较小的时候,直接使用limit较优。这个显然是子查询的原因。

2、offset大的时候。
select * from yanxue8_visit limit 10000,10

多次运行,时间保持在0.0187左右
Select * From yanxue8_visit Where vid >=(
Select vid From yanxue8_visit Order By vid limit 10000,1
) limit 10

多次运行,时间保持在0.0061左右,只有前者的1/3。可以预计offset越大,后者越优。

以后要注意改正自己的limit语句,优化一下mysql了

2009年3月17日星期二

Easy MySQL Performance Tweaks

This article is meant to be an easy and relatively safe way to enhance mysql performance. It is not meant to be a complete guide to tuning MySQL. Fully optimizing MySQL takes both time and effort since every application has different requirements. The Debian MySQL packages ship with very conservative memory usage settings and the same is probably true for other Linux distributions and the Windows binaries. If you loosen these settings up a little, MySQL will perform much faster under ordinary circumstances.

The items are ordered on impact, high to low. Feedback and hints will be greatly appreciated.

Key Buffer
The key buffer holds the indexes of tables in memory and a bigger key buffer results in faster row lookups. Adjust according to your own needs. Bigger is better, but prevent swapping at all costs. A good rule of thumb seems to be to use 1/4 of system memory.

key_buffer = 128M

Query Cache
This is where the magic happens. Well, not magic really, just plain old caching. Keeping the result of queries in memory until they are invalidated by additional writes enhances performance by magnitudes. The query_cache_size, as the name suggests, is the total size of memory available to query caching. The value query_cache_limit is the maximum number of kilobytes one query may be in order to be cached. Setting this value too high might prevent a lot of smaller queries to be cached. Setting it too low will result in bigger queries to never be cached, and the smaller queries not being able to completely fill the cache size, which would be a waste of resources. Adjust according to your own needs and memory available:

query_cache_size = 128MB
query_cache_limit = 4MB

Table Cache
An important variable if your application accesses many tables. It is the number of tables a thread can keep open at the same time. A value of 512 should do no harm.

table_cache = 512

Sort Buffers
sort_buffer_size (the variable previously known as sort_buffer), used for grouping and sorting and is a per-thread buffer. If the buffer can not hold the data to be sorted, a sort is performed on disk. Watch out for making this too large as the buffer is allocated for every thread that needs sorting and with many sorts it can easily consume all your memory.

sort_buffer_size = 32M
myisam_sort_buffer_size = 32M

The InnoDB Engine
Most people do not use the InnoDB engine in MySQL and use MyISAM instead. Since MySQL reserves memory for this engine, you are better off without it. If you need InnoDB, you can find more on its settings in the official MySQL docs.

Add `skip-innodb’ to my.cnf to disable the engine.

Binary Logging
MySQL has a few powerful features. Replicating data changes to a second server is one of them. MySQL keeps a log file of data changes which is used for this purpose. If you do not use replication or use the file as incremental backup, you can disable it. This will save you expensive disk write actions for every change to your data. For applications that have a lot of frequently updated data, this can be quite a performance boost. According to the official docs, this will generally result in just a 1% boost but it’s an easy gain if you do not need the log. Read more about the binary log here. Comment the following line:

log-bin = /var/log/mysql/mysql-bin.log

Temporary Tables
Temporary tables are used for sorting and grouping. The buffer is created on demand so watch out for setting this too high here as well. If the buffer cannot accomodate the data, a temp file is used on disk instead.

tmp_table_size = 64MB

Delayed Writing
This setting can greatly improve writing or updating data to a table. Instead of directly committing data to the disk, MySQL queues writes and returns write queries immediately. Be very very careful with this, because this also means that in case of a power failure or crash, you lose data. You can use this for logging if you don’t mind losing a couple of rows in case of a crash.

delay_key_write = 1

Connection Timeout
This is a little tweak that determines the closing of sleeping connections. The default is one hour and is often too long for practical purposes. I often set this at one minute instead (60).

wait_timeout = 60

The above settings are just to make mysql a little faster in general. You can get much better speed improvements by optimizing the database itself. Setting the correct indexes on tables can be a life-saver.

InnoDB和MyISAM的差别(mysql事务处理)

InnoDB 和MyISAM是在使用MySQL最常用的两个表类型,各有优缺点,视具体应用而定。基本的差别为:MyISAM类型不支持事务处理等高级处理,而 InnoDB类型支持。MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快,但是不提供事务支持,而InnoDB提供事务支持已经外部键等高级数据库功能。

MyIASM是IASM表的新版本,有如下扩展:
二进制层次的可移植性。
NULL列索引。
对变长行比ISAM表有更少的碎片。
支持大文件。
更好的索引压缩。
更好的键吗统计分布。
更好和更快的auto_increment处理。

1.MySQL最大的优势在于MyISAM引擎下的简单SELECT,INSERT和UPDATE快速操作
2.MyISAM类型的数据文件可以在不同操作系统中COPY,这点很重要,布署的时候方便点。

以下是一些细节和具体实现的差别:

1.InnoDB不支持FULLTEXT类型的索引。
2.InnoDB 中不保存表的具体行数,也就是说,执行select count(*) from table时,InnoDB要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count(*)语句包含 where条件时,两种表的操作是一样的。
3.对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引。
4.DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除。
5.LOAD TABLE FROM MASTER操作对InnoDB是不起作用的,解决方法是首先把InnoDB表改成MyISAM表,导入数据后再改成InnoDB表,但是对于使用的额外的InnoDB特性(例如外键)的表不适用。

另外,InnoDB表的行锁也不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,例如update table set num=1 where name like “%aaa%”

以暂对存储引擎的认识,觉得 InnoDB 支持外键,在数据量可以用“庞大”来形容时,在有良好的 INDEX 的基础上,InnoDB 的查询速度应该比 MyISAM 要快。
在 Falcon 有稳定版本前,我想 MyISAM 是一个可用的选择方案。

任何一种表都不是万能的,只用恰当的针对业务类型来选择合适的表类型,才能最大的发挥MySQL的性能优势。

MySQL中MyISAM引擎与InnoDB引擎性能简单测试

[硬件配置]
CPU : AMD2500+ (1.8G)
内存: 1G/现代
硬盘: 80G/IDE

[软件配置]
OS : Windows XP SP2
SE : PHP5.2.1
DB : MySQL5.0.37
Web: IIS6


[MySQL表结构]

CREATE TABLE `myisam` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(100) default NULL,
`content` text,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=gbk;

CREATE TABLE `innodb` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(100) default NULL,
`content` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk;

[数据内容]

$name = "heiyeluren";
$content = "MySQL支持数个存储引擎作为对不同表的类型的处理器。MySQL存储引擎包括处理事务安全表的引擎和处理非事务安全表的引擎:· MyISAM管理非事务表。它提供高速存储和检索,以及全文搜索能力。MyISAM在所有MySQL配置里被支持,它是默认的存储引擎,除非你配置MySQL默认使用另外一个引擎。 ·MEMORY存储引擎提供“内存中”表。MERGE存储引擎允许集合将被处理同样的MyISAM表作为一个单独的表。就像MyISAM一样,MEMORY和MERGE存储引擎处理非事务表,这两个引擎也都被默认包含在MySQL中。 释:MEMORY存储引擎正式地被确定为HEAP引擎。· InnoDB和BDB存储引擎提供事务安全表。BDB被包含在为支持它的操作系统发布的MySQL-Max二进制分发版里。InnoDB也默认被包括在所有MySQL 5.1二进制分发版里,你可以按照喜好通过配置MySQL来允许或禁止任一引擎。·EXAMPLE存储引擎是一个“存根”引擎,它不做什么。你可以用这个引擎创建表,但没有数据被存储于其中或从其中检索。这个引擎的目的是服务,在MySQL源代码中的一个例子,它演示说明如何开始编写新存储引擎。同样,它的主要兴趣是对开发者。";


[插入数据-1] (innodb_flush_log_at_trx_commit=1)
MyISAM 1W:3/s
InnoDB 1W:219/s

MyISAM 10W:29/s
InnoDB 10W:2092/s

MyISAM 100W:287/s
InnoDB 100W:没敢测试

[插入数据-2] (innodb_flush_log_at_trx_commit=0)
MyISAM 1W:3/s
InnoDB 1W:3/s

MyISAM 10W:30/s
InnoDB 10W:29/s

MyISAM 100W:273/s
InnoDB 100W:423/s

[插入数据3] (innodb_buffer_pool_size=1024M)
InnoDB 1W:3/s
InnoDB 10W:33/s
InnoDB 100W:607/s

[插入数据4] (innodb_buffer_pool_size=256M, innodb_flush_log_at_trx_commit=1, set autocommit=0)

InnoDB 1W:3/s
InnoDB 10W:26/s
InnoDB 100W:379/s



[MySQL 配置文件] (缺省配置)

# MySQL Server Instance Configuration File
[client]
port=3306

[mysql]
default-character-set=gbk

[mysqld]
port=3306
basedir="C:/mysql50/"
datadir="C:/mysql50/Data/"
default-character-set=gbk
default-storage-engine=INNODB
sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
max_connections=100

query_cache_size=0
table_cache=256
tmp_table_size=50M
thread_cache_size=8
myisam_max_sort_file_size=100G
myisam_max_extra_sort_file_size=100G
myisam_sort_buffer_size=100M
key_buffer_size=82M
read_buffer_size=64K
read_rnd_buffer_size=256K
sort_buffer_size=256K

innodb_additional_mem_pool_size=4M
innodb_flush_log_at_trx_commit=1
innodb_log_buffer_size=2M
innodb_buffer_pool_size=159M
innodb_log_file_size=80M
innodb_thread_concurrency=8



【总结】

可以看出在MySQL 5.0里面,MyISAM和InnoDB存储引擎性能差别并不是很大,针对InnoDB来说,影响性能的主要是 innodb_flush_log_at_trx_commit 这个选项,如果设置为1的话,那么每次插入数据的时候都会自动提交,导致性能急剧下降,应该是跟刷新日志有关系,设置为0效率能够看到明显提升,当然,同样你可以SQL中提交“SET AUTOCOMMIT = 0”来设置达到好的性能。另外,还听说通过设置innodb_buffer_pool_size能够提升InnoDB的性能,但是我测试发现没有特别明显的提升。

基本上我们可以考虑使用InnoDB来替代我们的MyISAM引擎了,因为InnoDB自身很多良好的特点,比如事务支持、存储过程、视图、行级锁定等等,在并发很多的情况下,相信InnoDB的表现肯定要比MyISAM强很多,当然,相应的在my.cnf中的配置也是比较关键的,良好的配置,能够有效的加速你的应用。

如果不是很复杂的Web应用,非关键应用,还是可以继续考虑MyISAM的,这个具体情况可以自己斟酌。


参考URL:
http://dev.mysql.com/doc/refman/5.1/zh/index.html
http://dev.mysql.com/doc/refman/5.1/zh/storage-engines.html#innodb

2009年3月9日星期一

公司地址

Top10SQLPerformanceTips

Interactive session from MySQL Camp I:

Specific Query Performance Tips (see also database design tips for tips on indexes):

1. Use EXPLAIN to profile the query execution plan
2. Use Slow Query Log (always have it on!)
3. Don't use DISTINCT when you have or could use GROUP BY
4. Insert performance
1. Batch INSERT and REPLACE
2. Use LOAD DATA instead of INSERT
5. LIMIT m,n may not be as fast as it sounds
6. Don't use ORDER BY RAND() if you have > ~2K records
7. Use SQL_NO_CACHE when you are SELECTing frequently updated data or large sets of data
8. Avoid wildcards at the start of LIKE queries
9. Avoid correlated subqueries and in select and where clause (try to avoid in)
10. No calculated comparisons -- isolate indexed columns
11. ORDER BY and LIMIT work best with equalities and covered indexes
12. Separate text/blobs from metadata, don't put text/blobs in results if you don't need them
13. Derived tables (subqueries in the FROM clause) can be useful for retrieving BLOBs without sorting them. (Self-join can speed up a query if 1st part finds the IDs and uses then to fetch the rest)
14. ALTER TABLE...ORDER BY can take data sorted chronologically and re-order it by a different field -- this can make queries on that field run faster (maybe this goes in indexing?)
15. Know when to split a complex query and join smaller ones
16. Delete small amounts at a time if you can
17. Make similar queries consistent so cache is used
18. Have good SQL query standards
19. Don't use deprecated features
20. Turning OR on multiple index fields (<5.0) into UNION may speed things up (with LIMIT), after 5.0 the index_merge should pick stuff up.
21. Don't use COUNT * on Innodb tables for every search, do it a few times and/or summary tables, or if you need it for the total # of rows, use SQL_CALC_FOUND_ROWS and SELECT FOUND_ROWS()
22. Use INSERT ... ON DUPLICATE KEY update (INSERT IGNORE) to avoid having to SELECT
23. use groupwise maximum instead of subqueries


Scaling Performance Tips:

1. Use benchmarking
2. isolate workloads don't let administrative work interfere with customer performance. (ie backups)
3. Debugging sucks, testing rocks!
4. As your data grows, indexing may change (cardinality and selectivity change). Structuring may want to change. Make your schema as modular as your code. Make your code able to scale. Plan and embrace change, and get developers to do the same.

Network Performance Tips:

1. Minimize traffic by fetching only what you need.
1. Paging/chunked data retrieval to limit
2. Don't use SELECT *
3. Be wary of lots of small quick queries if a longer query can be more efficient
2. Use multi_query if appropriate to reduce round-trips
3. Use stored procedures to avoid bandwidth wastage

OS Performance Tips:

1. Use proper data partitions
1. For Cluster. Start thinking about Cluster *before* you need them
2. Keep the database host as clean as possible. Do you really need a windowing system on that server?
3. Utilize the strengths of the OS
4. pare down cron scripts
5. create a test environment
6. source control schema and config files
7. for LVM innodb backups, restore to a different instance of MySQL so Innodb can roll forward
8. partition appropriately
9. partition your database when you have real data -- do not assume you know your dataset until you have real data

MySQL Server Overall Tips:

1. innodb_flush_commit=0 can help slave lag
2. Optimize for data types, use consistent data types. Use PROCEDURE ANALYSE() to help determine the smallest data type for your needs.
3. use optimistic locking, not pessimistic locking. try to use shared lock, not exclusive lock. share mode vs. FOR UPDATE
4. if you can, compress text/blobs
5. compress static data
6. don't back up static data as often
7. enable and increase the query and buffer caches if appropriate
8. config params -- http://docs.cellblue.nl/2007/03/17/easy-mysql-performance-tweaks/ is a good reference
9. Config variables & tips:
1. use one of the supplied config files
2. key_buffer, unix cache (leave some RAM free), per-connection variables, innodb memory variables
3. be aware of global vs. per-connection variables
4. check SHOW STATUS and SHOW VARIABLES (GLOBAL|SESSION in 5.0 and up)
5. be aware of swapping esp. with Linux, "swappiness" (bypass OS filecache for innodb data files, innodb_flush_method=O_DIRECT if possible (this is also OS specific))
6. defragment tables, rebuild indexes, do table maintenance
7. If you use innodb_flush_txn_commit=1, use a battery-backed hardware cache write controller
8. more RAM is good so faster disk speed
9. use 64-bit architectures
10. --skip-name-resolve
11. increase myisam_sort_buffer_size to optimize large inserts (this is a per-connection variable)
12. look up memory tuning parameter for on-insert caching
13. increase temp table size in a data warehousing environment (default is 32Mb) so it doesn't write to disk (also constrained by max_heap_table_size, default 16Mb)
14. Run in SQL_MODE=STRICT to help identify warnings
15. /tmp dir on battery-backed write cache
16. consider battery-backed RAM for innodb logfiles
17. use --safe-updates for client
18. Redundant data is redundant

Storage Engine Performance Tips:

1. InnoDB ALWAYS keeps the primary key as part of each index, so do not make the primary key very large
2. Utilize different storage engines on master/slave ie, if you need fulltext indexing on a table.
3. BLACKHOLE engine and replication is much faster than FEDERATED tables for things like logs.
4. Know your storage engines and what performs best for your needs, know that different ones exist.
1. ie, use MERGE tables ARCHIVE tables for logs
2. Archive old data -- don't be a pack-rat! 2 common engines for this are ARCHIVE tables and MERGE tables
5. use row-level instead of table-level locking for OLTP workloads
6. try out a few schemas and storage engines in your test environment before picking one.

Database Design Performance Tips:

1. Design sane query schemas. don't be afraid of table joins, often they are faster than denormalization
2. Don't use boolean flags
3. Use Indexes
4. Don't Index Everything
5. Do not duplicate indexes
6. Do not use large columns in indexes if the ratio of SELECTs:INSERTs is low.
7. be careful of redundant columns in an index or across indexes
8. Use a clever key and ORDER BY instead of MAX
9. Normalize first, and denormalize where appropriate.
10. Databases are not spreadsheets, even though Access really really looks like one. Then again, Access isn't a real database
11. use INET_ATON and INET_NTOA for IP addresses, not char or varchar
12. make it a habit to REVERSE() email addresses, so you can easily search domains (this will help avoid wildcards at the start of LIKE queries if you want to find everyone whose e-mail is in a certain domain)
13. A NULL data type can take more room to store than NOT NULL
14. Choose appropriate character sets & collations -- UTF16 will store each character in 2 bytes, whether it needs it or not, latin1 is faster than UTF8.
15. Use Triggers wisely
16. use min_rows and max_rows to specify approximate data size so space can be pre-allocated and reference points can be calculated.
17. Use HASH indexing for indexing across columns with similar data prefixes
18. Use myisam_pack_keys for int data
19. be able to change your schema without ruining functionality of your code
20. segregate tables/databases that benefit from different configuration variables

Other:

1. Hire a MySQL (tm) Certified DBA
2. Know that there are many consulting companies out there that can help, as well as MySQL's Professional Services.
3. Read and post to MySQL Planet at http://www.planetmysql.org
4. Attend the yearly MySQL Conference and Expo or other conferences with MySQL tracks (link to the conference here)
5. Support your local User Group (link to forge page w/user groups here)

Authored by

Jay Pipes, Sheeri Kritzer, Bill Karwin, Ronald Bradford, Farhan "Frank Mash" Mashraqi, Taso Du Val, Ron Hu, Klinton Lee, Rick James, Alan Kasindorf, Eric Bergen, Kaj Arno, Joel Seligstein, Amy Lee

2009年3月8日星期日

分析Windows和Linux动态库

原文见:http://tech.21ds.net/category/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/linux/
摘要:动态链接库技术实现和设计程序常用的技术,在Windows和Linux系统中都有动态库的概念,采用动态库可以有效的减少程序大小,节省空间,提高效率,增加程序的可扩展性,便于模块化管理。但不同操作系统的动态库由于格式不同,在需要不同操作系统调用时需要进行动态库程序移植。本文分析和比较了两种操作系统动态库技术,并给出了将Visual C++编制的动态库移植到Linux上的方法和经验。

 1、引言

 动态库(Dynamic Link Library abbr,DLL)技术是程序设计中经常采用的技术。其目的减少程序的大小,节省空间,提高效率,具有很高的灵活性。采用动态库技术对于升级软件版本更加容易。与静态库(Static Link Library)不同,动态库里面的函数不是执行程序本身的一部分,而是根据执行需要按需载入,其执行代码可以同时在多个程序中共享。

  在Windows和Linux操作系统中,都可采用这种方式进行软件设计,但他们的调用方式以及程序编制方式不尽相同。本文首先分析了在这两种操作系统中通常采用的动态库调用方法以及程序编制方式,然后分析比较了这两种方式的不同之处,最后根据实际移植程序经验,介绍了将VC++编制的 Windows动态库移植到Linux下的方法。

  2、动态库技术

  2.1 Windows动态库技术

  动态链接库是实现Windows应用程序共享资源、节省内存空间、提高使用效率的一个重要技术手段。常见的动态库包含外部函数和资源,也有一些动态库只包含资源,如Windows字体资源文件,称之为资源动态链接库。通常动态库以.dll,.drv、.fon等作为后缀。相应的windows静态库通常以.lib结尾,Windows自己就将一些主要的系统功能以动态库模块的形式实现。

  Windows动态库在运行时被系统加载到进程的虚拟空间中,使用从调用进程的虚拟地址空间分配的内存,成为调用进程的一部分。DLL也只能被该进程的线程所访问。DLL的句柄可以被调用进程使用;调用进程的句柄可以被DLL使用。DLL模块中包含各种导出函数,用于向外界提供服务。DLL可以有自己的数据段,但没有自己的堆栈,使用与调用它的应用程序相同的堆栈模式;一个DLL在内存中只有一个实例;DLL实现了代码封装性;DLL的编制与具体的编程语言及编译器无关,可以通过DLL来实现混合语言编程。DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。

  根据调用方式的不同,对动态库的调用可分为静态调用方式和动态调用方式。

  (1)静态调用,也称为隐式调用,由编译系统完成对DLL的加载和应用程序结束时DLL卸载的编码(Windows系统负责对DLL调用次数的计数),调用方式简单,能够满足通常的要求。通常采用的调用方式是把产生动态连接库时产生的.LIB文件加入到应用程序的工程中,想使用DLL中的函数时,只须在源文件中声明一下。 LIB文件包含了每一个DLL导出函数的符号名和可选择的标识号以及DLL文件名,不含有实际的代码。Lib文件包含的信息进入到生成的应用程序中,被调用的DLL文件会在应用程序加载时同时加载在到内存中。

  (2)动态调用,即显式调用方式,是由编程者用API函数加载和卸载DLL来达到调用DLL的目的,比较复杂,但能更加有效地使用内存,是编制大型应用程序时的重要方式。在Windows系统中,与动态库调用有关的函数包括:

  ①LoadLibrary(或MFC 的AfxLoadLibrary),装载动态库。
  ②GetProcAddress,获取要引入的函数,将符号名或标识号转换为DLL内部地址。
  ③FreeLibrary(或MFC的AfxFreeLibrary),释放动态链接库。

  在windows中创建动态库也非常方便和简单。在Visual C++中,可以创建不用MFC而直接用C语言写的DLL程序,也可以创建基于MFC类库的DLL程序。每一个DLL必须有一个入口点,在VC++中, DllMain是一个缺省的入口函数。DllMain负责初始化(Initialization)和结束(Termination)工作。动态库输出函数也有两种约定,分别是基于调用约定和名字修饰约定。DLL程序定义的函数分为内部函数和导出函数,动态库导出的函数供其它程序模块调用。通常可以有下面几种方法导出函数:

  ①采用模块定义文件的EXPORT部分指定要输入的函数或者变量。
  ②使用MFC提供的修饰符号_declspec(dllexport)。
  ③以命令行方式,采用/EXPORT命令行输出有关函数。

  在windows动态库中,有时需要编写模块定义文件(.DEF),它是用于描述DLL属性的模块语句组成的文本文件。

 2.2 Linux共享对象技术


  在Linux操作系统中,采用了很多共享对象技术(Shared Object),虽然它和Windows里的动态库相对应,但它并不称为动态库。相应的共享对象文件以.so作为后缀,为了方便,在本文中,对该概念不进行专门区分。Linux系统的/lib以及标准图形界面的/usr/X11R6/lib等目录里面,就有许多以so结尾的共享对象。同样,在Linux 下,也有静态函数库这种调用方式,相应的后缀以.a结束。Linux采用该共享对象技术以方便程序间共享,节省程序占有空间,增加程序的可扩展性和灵活性。Linux还可以通过LD-PRELOAD变量让开发人员可以使用自己的程序库中的模块来替换系统模块。

  同Windows系统一样,在Linux中创建和使用动态库是比较容易的事情,在编译函数库源程序时加上-shared选项即可,这样所生成的执行程序就是动态链接库。通常这样的程序以so为后缀,在Linux动态库程序设计过程中,通常流程是编写用户的接口文件,通常是.h文件,编写实际的函数文件,以.c或.cpp为后缀,再编写makefile文件。对于较小的动态库程序可以不用如此,但这样设计使程序更加合理。

  编译生成动态连接库后,进而可以在程序中进行调用。在Linux中,可以采用多种调用方式,同Windows的系统目录(..\ system32等)一样,可以将动态库文件拷贝到/lib目录或者在/lib目录里面建立符号连接,以便所有用户使用。下面介绍Linux调用动态库经常使用的函数,但在使用动态库时,源程序必须包含dlfcn.h头文件,该文件定义调用动态链接库的函数的原型。

  (1)_打开动态链接库:dlopen,函数原型void *dlopen (const char *filename, int flag);
dlopen用于打开指定名字(filename)的动态链接库,并返回操作句柄。

  (2)取函数执行地址:dlsym,函数原型为: void *dlsym(void *handle, char *symbol);
dlsym根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的函数的执行代码地址。

  (3)关闭动态链接库:dlclose,函数原型为: int dlclose (void *handle);
dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。

  (4)动态库错误函数:dlerror,函数原型为: const char *dlerror(void); 当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。

  在取到函数执行地址后,就可以在动态库的使用程序里面根据动态库提供的函数接口声明调用动态库里面的函数。在编写调用动态库的程序的makefile文件时,需要加入编译选项-rdynamic和-ldl。

  除了采用这种方式编写和调用动态库之外,Linux操作系统也提供了一种更为方便的动态库调用方式,也方便了其它程序调用,这种方式与 Windows系统的隐式链接类似。其动态库命名方式为“lib*.so.*”。在这个命名方式中,第一个*表示动态链接库的库名,第二个*通常表示该动态库的版本号,也可以没有版本号。在这种调用方式中,需要维护动态链接库的配置文件/etc/ld.so.conf来让动态链接库为系统所使用,通常将动态链接库所在目录名追加到动态链接库配置文件中。如具有X window窗口系统发行版该文件中都具有/usr/X11R6/lib,它指向X window窗口系统的动态链接库所在目录。为了使动态链接库能为系统所共享,还需运行动态链接库的管理命令./sbin/ldconfig。在编译所引用的动态库时,可以在gcc采用 ?l或-L选项或直接引用所需的动态链接库方式进行编译。在Linux里面,可以采用ldd命令来检查程序依赖共享库。

3、两种系统动态库比较分析


  Windows和Linux采用动态链接库技术目的是基本一致的,但由于操作系统的不同,他们在许多方面还是不尽相同,下面从以下几个方面进行阐述。

  (1)动态库程序编写,在Windows系统下的执行文件格式是PE格式,动态库需要一个DllMain函数作为初始化的人口,通常在导出函数的声明时需要有_declspec(dllexport)关键字。Linux下的gcc编译的执行文件默认是ELF格式,不需要初始化入口,亦不需要到函数做特别声明,编写比较方便。

  (2)动态库编译,在windows系统下面,有方便的调试编译环境,通常不用自己去编写makefile文件,但在linux下面,需要自己动手去编写makefile文件,因此,必须掌握一定的makefile编写技巧,另外,通常Linux编译规则相对严格。

  (3)动态库调用方面,Windows和Linux对其下编制的动态库都可以采用显式调用或隐式调用,但具体的调用方式也不尽相同。

  (4)动态库输出函数查看,在Windows中,有许多工具和软件可以进行查看DLL中所输出的函数,例如命令行方式的dumpbin以及VC ++工具中的DEPENDS程序。在Linux系统中通常采用nm来查看输出函数,也可以使用ldd查看程序隐式链接的共享对象文件。

  (5)对操作系统的依赖,这两种动态库运行依赖于各自的操作系统,不能跨平台使用。因此,对于实现相同功能的动态库,必须为两种不同的操作系统提供不同的动态库版本。

4、动态库移植方法


  如果要编制在两个系统中都能使用的动态链接库,通常会先选择在Windows的VC++提供的调试环境中完成初始的开发,毕竟VC++ 提供的图形化编辑和调试界面比vi和gcc方便许多。完成测试之后,再进行动态库的程序移植。通常gcc默认的编译规则比VC++默认的编译规则严格,即使在VC++下面没有任何警告错误的程序在gcc调试中也会出现许多警告错误,可以在gcc中采用-w选项关闭警告错误。

  下面给出程序移植需要遵循的规则以及经验。

  (1)尽量不要改变原有动态库头文件的顺序。通常在C/C++语言中,头文件的顺序有相当的关系。另外虽然C/C++语言区分大小写,但在包含头文件时,Linux必须与头文件的大小写相同,因为ext2文件系统对文件名是大小写敏感,否则不能正确编译,而在Windows下面,头文件大小写可以正确编译。

  (2)不同系统独有的头文件。在Windows系统中,通常会包括windows.h头文件,如果调用底层的通信函数,则会包含 winsock..h头文件。因此在移植到Linux系统时,要注释掉这些Windows系统独有的头文件以及一些windows系统的常量定义说明,增加Linux都底层通信的支持的头文件等。

  (3)数据类型。VC++具有许多独有的数据类型,如__int16,__int32, TRUE,SOCKET等,gcc编译器不支持它们。通常做法是需要将windows.h和basetypes.h中对这些数据进行定义的语句复制到一个头文件中,再在Linux中包含这个头文件。例如将套接字的类型为SOCKET改为int。

  (4)关键字。VC++中具有许多标准C中所没有采用的关键字,如BOOL,BYTE,DWORD,__asm等,通常在为了移植方便,尽量不使用它们,如果实在无法避免可以采用#ifdef 和#endif为LINUX和WINDOWS编写两个版本。
  (5)函数原型的修改。通常如果采用标准的C/C++语言编写的动态库,基本上不用再重新编写函数,但对于系统调用函数,由于两种系统的区别,需要改变函数的调用方式等,如在Linux编制的网络通信动态库中,用close()函数代替windows操作系统下的closesocket()函数来关闭套接字。另外在Linux下没有文件句柄,要打开文件可用open和fopen函数,具体这两个函数的用法可参考文献[2]。

  (6) makefile的编写。在windows下面通常由VC++编译器来负责调试,但gcc需要自己动手编写makefile文件,也可以参照VC++生成的makefile文件。对于动态库移植,编译动态库时需要加入-shared选项。对于采用数学函数,如幂级数的程序,在调用动态库是,需要加入- lm。

  (7)其它一些需要注意的地方

  ①程序设计结构分析,对于移植它人编写的动态库程序,程序结构分析是必不可少的步骤,通常在动态库程序中,不会包含界面等操作,所以相对容易一些。
  ②在Linux中,对文件或目录的权限分为拥有者、群组、其它。所以在存取文件时,要注意对文件是读还是写操作,如果是对文件进行写操作,要注意修改文件或目录的权限,否则无法对文件进行写。
  ③指针的使用,定义一个指针只给它分配四个字节的内存,如果要对指针所指向的变量赋值,必须用malloc函数为它分配内存或不把它定义为指针而定义为变量即可,这点在linux下面比windows编译严格。同样结构不能在函数中传值,如果要在函数中进行结构传值,必须把函数中的结构定义为结构指针。
  ④路径标识符,在Linux下是“/”,在Windows下是“\”,注意windows和Linux的对动态库搜索路径的不同。
  ⑤编程和调试技巧方面。对不同的调试环境有不同的调试技巧,在这里不多叙述。

  5、结束语

  本文系统分析了windows和Linux动态库实现和使用方式,从程序编写、编译、调用以及对操作系统依赖等方面综合分析比较了这两种调用方式的不同之处,根据实际程序移植经验,给出了将VC++编制的Windows动态库移植到Linux下的方法以及需要注意的问题,同时并给出了程序示例片断,实际在程序移植过程中,由于系统的设计等方面,可能移植起来需要注意的方面远比上面复杂,本文通过总结归纳进而为不同操作系统程序移植提供了有意的经验和技巧。

2009年3月6日星期五

2009年3月5日星期四

Usefull Commands: Video

Published
by
kev
2 years, 3 months ago
in English


* Change the aspect ratio of a film for the playback. Standard aspect ratio are : 1.33 (4:3), 1.66 (1.66:1), 1.77 (16:9) and 2.35 (2.35:1):

mplayer -aspect 2:1 ./video.avi

* Play the video with subtitles:

mplayer -sub ./subtitle_file.txt ./video.avi

* This will extract audio track no. 128, downmix the AC3 sound to PCM and write the results to file.wav:

mplayer -vo null -hardframedrop -aid 128 -ao pcm -aofile file.wav dvd://1

* This will extract the audio, convert it to PCM and write the resulting wave file to audio.wav:

mplayer -vo null -hardframedrop -ao pcm:file=audio.wav myvideo.avi

* Extract to chapter.txt the chapter file of the track n°1 of the DVD:

dvdxchap -t 1 /mnt/cdrom > chapter.txt

* Show all subtitles streams:

mplayer -vo null -ao null -frames 0 -v 2 dvd://1 >&1 | grep sid

* Extract the raw subtitle stream. The -a 0x21 option correspond to the subtitle stream’s hexadecimal number (= 0×20 + id of the stream):

tccat -i /space/st-tng/dic1/ -T 1 -L | tcextract -x ps1 -t vob -a 0x22 > subs-en

* Create a rotated copy of the file.avi video (rotate=1 : clockwise ; rotate=2 : anti-clockwise):

mencoder -vop rotate=2 -oac pcm -ovc lavc ./source.avi -o ./dest.avi

* Preview a video composed of all jpeg files from the current folder at 15fps (mplayer only support jpeg, png, tga and sgi formats):

mplayer "mf://*.jpg" -mf fps=15

* Create a 15fps video from all jpeg files of the current folder:

mencoder "mf://*.jpg" -mf fps=15 -ovc lavc -o ./dest.avi

* Encode a SVCD to AVI file:

mencoder -oac lavc -ovc lavc vcd://1 -o ./svcd.avi

* Transcode video to raw format (be carefull: usually the output video got annoying audio delay):

mencoder -oac pcm -ovc raw -ofps 25 -noskip ./video.wmv -o ./video.avi

* Encode a video using the default mpeg4 codec at 400 kbps for video and mp3 codec at constant 32 kbps bitrate for audio:

mencoder -oac mp3lame -lameopts cbr:preset=32 -ovc lavc -lavcopts vbitrate=400 in.avi -o out.avi

* Enhance the sharpness of the video:

mplayer video.avi -vf smartblur=.6:-.5:0,unsharp=l5x5:.8:c5x5:.4

* Merge multiple video into one:

avimerge -i part1.avi part2.avi -o big-file.avi

* Cut a video to keep the first 5.4 seconds:

mencoder big-file.avi -ss 0 -endpos 5.4 -ovc copy -oac copy -o cutted.avi

* Cut a video to keep everything exept the first 5.4 seconds:

mencoder big-file.avi -ss 5.4 -ovc copy -oac copy -o cutted.avi

* Show all mplayer filter list:

mplayer -vf help

* Get help of a particular filter (eq2 in this example):

mplayer -vf eq2=help

* Here is the filter I use to light up a video taken in the dark with my cheap camera. Of course it add noise but thanks to this we can distinguish shapes in the dark.

mencoder -vf eq2=1.61:1.95:0.54:2.43 -oac pcm -ovc lavc video.avi -o bright-vid.avi

* And this is the command to preview the result of the filter used above:

mplayer video.avi -vf eq2=1.61:1.95:0.54:2.43

* This is how I convert raw videos taken with my digital camera into ISO standard MPEG-4 (DivX 5, XVID compatible) videos [to encode in grayscale, add :gray option to -lavcopts]:

mencoder source.avi \
-ovc lavc -oac lavc -ffourcc DX50 \
-lavcopts vcodec=mpeg4:vbitrate=400:v4mv:mbd=2:trell:autoaspect:dia=2:acodec=mp3:abitrate=32:vpass=1 \
-vf hqdn3d -o output.avi
mencoder source.avi \
-ovc lavc -oac lavc -ffourcc DX50 \
-lavcopts vcodec=mpeg4:vbitrate=400:v4mv:mbd=2:trell:autoaspect:dia=2:acodec=mp3:abitrate=32:vpass=2 \
-vf hqdn3d -o output.avi

* Play all videos of the current folder fullscreen at 4x speed with 50% more brightness:

mplayer -speed 4 -brightness 50 -fs ./*.avi

* Extract audio stream from a video:

mplayer -dumpaudio -dumpfile audio.ac3 video_source.mpg

* Test XV video driver output via gstreamer v0.10:

gst-launch-0.10 videotestsrc ! xvimagesink

mplayer bmovl

mplayer -vf

video filter: bmovl

bmovl=隐藏:不透明:<命名管道>

从一个命名管道读取位图并把它们显示在窗口中.

隐藏: 设置’隐藏’标记的默认值(布尔值)
不透明: 切换alphablended(透明)和不透明(快速)模式标记
命 名管道: 命名管道的路径/文件名(连接mplayer -vf bmovl 和控制程序的命名管道)

命名管道命令有:

RGBA32 width height xpos ypos alpha clear
接受width*height*4字节的原始RGBA32数据

ABGR32 width height xpos ypos alpha clear
接受width*height*4字节的原始ABGR32 data.

RGB24 width height xpos ypos alpha clear
接受width*height*3字节的原始RGB32 data.

BGR24 width height xpos ypos alpha clear
接受width*height*3字节的原始BGR32 data.

ALPHA width height xpos ypos alpha
改变区域的alpha值

CLEAR width height xpos ypos
清除数据

OPAQUE
禁用所有alpha透明发送"ALPHA 0 0 0 0 0"可以重新打开 它.

HIDE
隐藏位图

SHOW
显示位图

参数有:

width, height: 图像/区域尺寸
xpos, ypos: 位图传送的X/Y位置
alpha: 设置alpha差别. 0标识原始值, 255使所有都不透明, -255使所有都透明. 如果你把它设为-255, 你可以随后发 送 一 个ALPHA命令序列吧区域设置为-225, -200, -175等等来获 得一个漂亮的淡入效果! ;)
clear: 传送前清楚帧缓冲. 1表示清除, 如果是0, 图像会被 传送到老图像上, 所以你不需要每次为屏幕小部分的变化都发 送1,8MB的RGBA32数据.

mplayer -vf

Available video filters:

rectangle : draw rectangle

bmovl : Read bitmaps from a FIFO and display them in window

crop : cropping

expand : expanding & osd

pp : postprocessing

scale : software scaling

vo : libvo wrapper

format : force output format

noformat : disallow one output format

yuy2 : fast YV12/Y422p -> YUY2 conversion

flip : flip image upside-down

rgb2bgr : fast 24/32bpp RGB<->BGR conversion

rotate : rotate

mirror : horizontal mirror

palette : 8bpp indexed (using palette) -> BGR 15/16/24/32 conversion

lavc : realtime mpeg1 encoding with libavcodec

lavcdeint : libavcodec's deinterlacing filter

pp7 : postprocess 7

dvbscale : calc Y scaling for DVB card

cropdetect : autodetect crop size

test : test pattern generator

noise : noise generator

yvu9 : fast YVU9->YV12 conversion

eq : soft video equalizer

eq2 : Software equalizer

halfpack : yuv planar 4:2:0 -> packed 4:2:2, half height

dint : drop interlaced frames

1bpp : 1bpp bitmap -> YUV/BGR 8/15/16/32 conversion

2xsai : 2xSai BGR bitmap 2x scaler

unsharp : unsharp mask & gaussian blur

swapuv : UV swapper

il : (de)interleave

fil : fast (de)interleaver

boxblur : box blur

sab : shape adaptive blur

smartblur : smart blur
bmovl
perspective : perspective correcture

down3dright : convert stereo movie from top-bottom to left-right field

field : extract single field

denoise3d : 3D Denoiser (variable lowpass filter)

hqdn3d : High Quality 3D Denoiser

detc : de-telecine filter

telecine : telecine filter

tinterlace : temporal field interlacing

tfields : temporal field separation

ivtc : inverse telecine, take 2

ilpack : 4:2:0 planar -> 4:2:2 packed reinterlacer

dsize : reset displaysize/aspect

decimate : near-duplicate frame remover

softpulldown : mpeg2 soft 3:2 pulldown

pullup : pullup (from field sequence to frames)

filmdint : Advanced inverse telecine filer

framestep : Dump one every n / key frames

tile : Make a single image tiling x/y images

delogo : simple logo remover

remove-logo : Removes a tv logo based on a mask image.

hue : hue changer

spp : simple postprocess

uspp : ultra simple/slow postprocess

fspp : fast simple postprocess

qp : QP changer

mcdeint : motion compensating deinterlacer

geq : generic equation filter

yuvcsp : yuv colorspace converter

kerndeint : Kernel Deinterlacer

rgbtest : rgbtest

phase : phase shift fields

divtc : inverse telecine for deinterlaced video

harddup : resubmit duplicate frames for encoding

softskip : soft (post-filter) frame skipping for encoding

screenshot : screenshot to file

ass : Render ASS/SSA subtitles

yadif : Yet Another DeInterlacing Filter

blackframe : detects black frames

ow : overcomplete wavelet denoiser

mplayer 半透明 滤镜融合

overlay
http://urandom.ca/mebox/downloads/vf_overlay.txt


mplayer bvmol
http://www.mplayerhq.hu/DOCS/man/zh/mplayer.1.html


官方手册

http://www.mplayerhq.hu/DOCS/HTML-single/zh_CN/MPlayer.html

http://www.mplayerhq.hu/DOCS/man/zh/mplayer.1.html



bmovl=hidden:opaque:fifo

http://www.sit.auckland.ac.nz/Video_overlay_with_mplayer

apt-get install -y xserver-xorg libraw1394-8 libmpeg2-4 mpeg2dec ffmpeg pciutils less xfonts-base vlc coriander libsdl1.2-dev

Various cut and paste command lines.

mplayer -vo x11 -fs -display 0:0 -vf scale=1360:-2 Nosferatu.mp4
mplayer -vo x11 -fs -display 0:0 -vf scale=1024:-2 Nosferatu.mp4

bmovl=hidden:opaque:fifo

-vf bmovl

http://projects.sault.org/mebox/downloads/patches/vf_overlay_outbuf-2008-07-07-TESTING.diff

2009年3月1日星期日

可怕的9城服务条款

在注册9诚通行证时,看到以下条款,感觉个人完全没有安全感。

4.1 对于用户所登录留存的个人资料,除下列情形外,第九城市同意在未得到用户同意前,不公开对外披露:
a.基于法律规定
b.基于司法机关或其它有权机关基于法定程序的要求
c.为保障第九城市的财产及权益
d.在紧急情况下为保护其它用户或第三人的人身安全的情形下

4.2 对于用户所登录的个人资料,用户同意第九城市及其关系企业或合作对象,可以在合理范围内搜集、处理、保存、传递及使用该资料,以提供用户其它信息及服务或做成会计资料,或进行网络行为的调查或研究,或其它任何合法使用。

4.3 第九城市无法保证不将用户的个人资料或通讯内容给予第三者。举例来说,如果政府需要资料时第九城市将被迫交出,或者是有其它的第三者拦截网络的传输内容。 此外,用户授权第九城市可将用户的资料给予执法机关或政府相关单位,以利调查或问题解决的进行。除此之外,当用户要求第九城市给予任何技术上的协助时,用 户授权第九城市远程检视与更改用户计算机的内容。为了更新程序的需要,用户授权第九城市(i)从用户的计算机上传档案(ii)下载档案到用户的计算机。

2009年2月26日星期四

My favorite wallpaper

2009年2月17日星期二

Mplayer的代码分析、架构整理,从这里开始

由于工作需要,在接下来的日子里,我要对mplayer进行代码分析,最终整理出它的框架。
mplayer,是一个非常风靡的播放器,从开发到应用都非常普遍,奇怪的是在网上找不到它的技术文档,一丁点也找不到。所以,我在这里整理一点,对自己和大家将来研究和使用它都应该很有帮助。
如果你有相关的东西,请不吝贴到这里来,我也表示万分感谢。

2009年2月11日星期三

(抄袭)如何写好概要设计

在需求明确、准备开始编码之前,要做概要设计,而详细设计可能大部分公司没有做,有做的也大部分是和编码同步进行,或者在编码之后。因此,对大部分的公司来说,概要设计文档是唯一的设计文档,对后面的开发、测试、实施、维护工作起到关键性的影响。

一、问题的提出

概要设计写什么?概要设计怎么做?

如何判断设计的模块是完整的?

为什么说设计阶段过于重视业务流程是个误区?

以需求分析文档还是以概要设计文档来评估开发工作量、指导开发计划准确?

结构化好还是面向对象好?

以上问题的答案请在文章中找。

二、概要设计的目的

将软件系统需求转换为未来系统的设计;

逐步开发强壮的系统构架;

使设计适合于实施环境,为提高性能而进行设计;

结构应该被分解为模块和库。

三、概要设计的任务

制定规范:代码体系、接口规约、命名规则。这是项目小组今后共同作战的基础,有了开发规范和程序模块之间和项目成员彼此之间的接口规则、方式方法,大家就有了共同的工作语言、共同的工作平台,使整个软件开发工作可以协调有序地进行。

总体结构设计:

功能(加工)->模块:每个功能用那些模块实现,保证每个功能都有相应的模块来实现;

模块层次结构:某个角度的软件框架视图;

模块间的调用关系:模块间的接口的总体描述;

模块间的接口:传递的信息及其结构;

处理方式设计:满足功能和性能的算法

用户界面设计;

数据结构设计:

详细的数据结构:表、索引、文件;

算法相关逻辑数据结构及其操作;

上述操作的程序模块说明(在前台?在后台?用视图?用过程?······)

接口控制表的数据结构和使用规则

其他性能设计。

四、概要设计写什么

结构化软件设计说明书结构(因篇幅有限和过时嫌疑,在此不作过多解释)

任务:目标、环境、需求、局限;

总体设计:处理流程、总体结构与模块、功能与模块的关系;

接口设计:总体说明外部用户、软、硬件接口;内部模块间接口(注:接口≈系统界面)

数据结构:逻辑结构、物理结构,与程序结构的关系;

模块设计:每个模块“做什么”、简要说明“怎么做”(输入、输出、处理逻辑、与其它模块的接口,与其它系统或硬件的接口),处在什么逻辑位置、物理位置;

运行设计:运行模块组合、控制、时间;

出错设计:出错信息、处错处理;

其他设计:保密、维护;

OO软件设计说明书结构

1 概述

系统简述、软件设计目标、参考资料、修订版本记录

这部分论述整个系统的设计目标,明确地说明哪些功能是系统决定实现而哪些时不准备实现的。同时,对于非功能性的需求例如性能、可用性等,亦需提及。需求规格说明书对于这部分的内容来说是很重要的参考,看看其中明确了的功能性以及非功能性的需求。

这部分必须说清楚设计的全貌如何,务必使读者看后知道将实现的系统有什么特点和功能。在随后的文档部分,将解释设计是怎么来实现这些的。

2 术语表

对本文档中所使用的各种术语进行说明。如果一些术语在需求规格说明书中已经说明过了,此处不用再重复,可以指引读者参考需求说明。

3 用例

此处要求系统用用例图表述(UML),对每个用例(正常处理的情况)要有中文叙述。

4 设计概述

4.1 简述

这部分要求突出整个设计所采用的方法(是面向对象设计还是结构化设计)、系统的体系结构(例如客户/服务器结构)以及使用到的相应技术和工具(例如OMT、Rose)

4.2 系统结构设计

这部分要求提供高层系统结构(顶层系统结构、各子系统结构)的描述,使用方框图来显示主要的组件及组件间的交互。最好是把逻辑结构同物理结构分离,对前者进行描述。别忘了说明图中用到的俗语和符号。

4.3 系统界面

各种提供给用户的界面以及外部系统在此处要予以说明。如果在需求规格说明书中已经对用户界面有了叙述,此处不用再重复,可以指引读者参考需求说明。如果系统提供了对其它系统的接口,比如说从其它软件系统导入/导出数据,必须在此说明。

4.4 约束和假定

描述系统设计中最主要的约束,这些是由客户强制要求并在需求说明书写明的。说明系统是如何来适应这些约束的。

另外如果本系统跟其它外部系统交互或者依赖其它外部系统提供一些功能辅助,那么系统可能还受到其它的约束。这种情况下,要求清楚地描述与本系统有交互的软件类型以及这样导致的约束。

实现的语言和平台也会对系统有约束,同样在此予以说明。

对于因选择具体的设计实现而导致对系统的约束,简要地描述你的想法思路,经过怎么样的权衡,为什么要采取这样的设计等等。

5 对象模型

提供整个系统的对象模型,如果模型过大,按照 可行的标准把它划分成小块,例如可以把客户端和服务器端的对象模型分开成两个图表述。在其中应该包含所有的系统对象。这些对象都是从理解需求后得到的。要 明确哪些应该、哪些不应该被放进图中。所有对象之间的关联必须被确定并且必须指明联系的基数。聚合和继承关系必须清楚地确定下来。每个图必须附有简单的说 明。

6 对象描述

在这个部分叙述每个对象的细节,它的属性、它的方法。在这之前必须从逻辑上对对象进行组织。你可能需要用结构图把对象按子系统划分好。

为每个对象做一个条目。在系统对象模型中简要的描述它的用途、约束(如只能有一个实例),列出它的属性和方法。如果对象是存储在持久的数据容器中,标明它是持久对象,否则说明它是个临时对象(transient object)。

对每个对象的每个属性详细说明:名字、类型,如果属性不是很直观或者有约束(例如,每个对象的该属性必须有一个唯一的值或者值域是有限正整数等)。

对每个对象的每个方法详细说明:方法名,返回 类型,返回值,参数,用途以及使用的算法的简要说明(如果不是特别简单的话)。如果对变量或者返回值由什么假定的话,Pre-conditions和 Post-conditions必须在此说明。列出它或者被它调用的方法需要访问或者修改的属性。最后,提供可以验证实现方法的测试案例。

7 动态模型

这部分的作用是描述系统如何响应各种事件。一般使用顺序图和状态图。

确定不同的场景(Scenario)是第一步,不需要确定所有可能的场景,但是必须至少要覆盖典型的系统用例。不要自己去想当然地创造场景,通常的策略是描述那些客户可以感受得到的场景。

7.1 场景(Scenarios)

对每个场景做一则条目,包括以下内容:

场景名:给它一个可以望文生义的名字

场景描述:简要叙述场景是干什么的以及发生的动作的顺序。

顺序图:描述各种事件及事件发生的相对时间顺序。

7.2 状态图

这部分的内容包括系统动态模型重要的部分的状态图。可能你想为每个对象画一个状态图,但事实上会导致太多不期望的细节信息,只需要确定系统中一些重要的对象并为之提供状态图即可。

8 非功能性需求

五、概要设计怎么做

结构化软件设计方法:

详细阅读需求规格说明书,理解系统建设目标、业务现状、现有系统、客户需求的各功能说明;

分析数据流图,弄清数据流加工的过程;

根据数据流图决定数据处理问题的类型(变换型、事务型、其他型);

通过以上分析,推导出系统的初始结构图;

对初始结构图进行改进完善:所有的加工都要能对应到相应模块(模块的完整性在于他们完成了需求中的所有加工),消除完全相似或局部相似的重复功能(智者察同),理清模块间的层次、控制关系,减少高扇出结构,随着深度增大扇入,平衡模块大小。

由对数据字典的修改补充完善,导出逻辑数据结构,导出每种数据结构上的操作,这些操作应当属于某个模块。

确定系统包含哪些应用服务系统、客户端、数据库管理系统;

确定每个模块放在哪个应用服务器或客户端的哪个目录、哪个文件(库),或是在数据库内部建立的对象。

对每个筛选后的模块进行列表说明。

对逻辑数据结构进行列表说明。

根据结构化软件设计说明书结构对其他需要说明的问题进行补充说明,形成概要设计说明书。

OO软件设计方法:

在OOA基础上设计对象与类:在问题领域分析(业务建模和需求分析)之后,开始建立系统构架。

第一步是抽取建立领域的概念模型,在UML中表现为建立对象类图、活动图和交互图。对象类就是从对象中经过“察同”找出某组对象之间的共同特征而形成类:

对象与类的属性:数据结构;

对象与类的服务操作:操作的实现算法;

对象与类的各外部联系的实现结构;

设计策略:充分利用现有的类;

方法:继承、复用、演化;

活动图用于定义工作流,主要说明工作流的5W(Do What、Who Do、When Do、Where Do、Why Do)等问题,交互图把人员和业务联系在一起是为了理解交互过程,发现业务工作流中相互交互的各种角色。

第二步是构建完善系统结构:对系统进行分解,将大系统分解为若干子系统,子系统分解为若干软件组件,并说明子系统之间的静态和动态接口,每个子系统可以由用例模型、分析模型、设计模型、测试模型表示。软件系统结构的两种方式:层次、块状

层次结构:系统、子系统、模块、组件(同一层之间具有独立性);

块状结构:相互之间弱耦合

系统的组成部分:

问题论域:业务相关类和对象(OOA的重点);

人机界面:窗口、菜单、按钮、命令等等;

数据管理:数据管理方法、逻辑物理结构、操作对象类;

任务管理:任务协调和管理进程;

第三步是利用“4+1”视图描述系统架构:用 例视图及剧本;说明体系结构的设计视图;以模块形式组成包和层包含概要实现模型的实现视图;说明进程与线程及其架构、分配和相互交互关系的过程视图;说明 系统在操作平台上的物理节点和其上的任务分配的配置视图。在RUP中还有可选的数据视图。

第四步是性能优化(速度、资源、内存)、模型清晰化、简单化(简单就是享受)。

六、概要设计的原则

总体原则和方法:由粗到细的原则,互相结合的原则,定性分析和定量分析相结合的方法,分解和协调的方法和模型化方法。

要系统考虑系统的一般性、关联性、整体性和层次性。

分解协调:目的是为了创造更好的系统。系统分 解是指将一个复杂的系统分解为若干个子系统,系统协调一是系统内协调,即根据系统的总结构、总功能、总任务和总目标的要求,使各个子系统之间互相协调配 合,在各个子系统局部优化基础上,通过内部平衡的协调控制,实现系统的整体优化;

屏蔽抽象:从简单的框架开始,隐含细节;

一致性:统一的规范、统一的标准、统一的文件模式;

每个模块应当有一个统一命名的容易理解的名字;

编码:由外向内(界面->核心);

面向用户:概要设计是对于按钮按下后系统“怎么做”的简要说明;

模块、组件的充分独立性、封闭性;

同时考虑静态结构与动态运行;

每个逻辑对象都应当说明其所处物理对象(非一一对应);

每个物理对象都有合适的开发人员,并且利于分工与组装。(详细说明见本人另一篇文章:系统构架设计应考虑的因素);

确立每个构架视图的整体结构:视图的详细组织结构、元素的分组以及这些主要分组之间的接口;

软件构架与使用的技术平台密切相关,目前常用的平台有J2EE、.NET、CORBA等等,因此具体的软件构架人员应当具备使用这些平台的软件开发经验;

通过需求功能与设计模块之间的列表对应,检查每个需求功能是否都有相应的模块来实现,保证需求功能的可追溯性和需求实现(模块)的完整性,同时可以检查重复和不必要的模块。

在需求调研分析过程中对业务处理过程了解的完 整性和准确性非常重要。调查了解清楚所有的业务流程才能设计出适合各流程业务节点用户业务特点和习惯的软件,使开发出来的软件更受欢迎。当然在进行软件概 要设计时,要尽量排除业务流程的制约,即把流程中的各项业务结点工作作为独立的对象,设计成独立的模块,充分考虑他们与其他各种业务对象模块的接口,在流 程之间通过业务对象模块的相互调用实现各种业务,这样,在业务流程发生有限的变化时(每个业务模块本身的业务逻辑没有变的情况下),就能够比较方便地修改 系统程序模块间的调用关系而实现新的需求。如果这种调用关系被设计成存储在配置库的数据字典里,则连程序代码都不用修改,只需修改数据字典里的模块调用规 则即可。

七、概要设计的重要输出

编码规范:信息形式、接口规约、命名规则;

物理模型:组件图、配置图;

不同角度的构架视图:用例视图、逻辑视图、进程视图、部署视图、实施视图、数据视图(可选);

系统总体布局:哪些部分组成、各部分在物理上、逻辑上的相互关系;

两个不可忽视的输出:

与需求功能的关系:对于需求中的每一个功能, 用哪一层、哪个模块、哪个类、哪个对象来实现(一对多关系);反过来,应当说明将要创建的系统每一层、每个模块、每个对象、每一个类“做什么”,他们是为 了帮助实现哪些功能(一对多关系)。(需求的颗粒度在一开始往往是比较粗的,因此根据功能点对于整体项目规模的估计或得到项目WBS其误差范围也是比较大 的。更为重要的原因是,需求往往不是编码工作分解的准确依据,因为一个需求的功能点可能对应多个代码模块,而多个需求的功能点也可能只对应一个或少数代码 模块,同时还有软件复用等因素要考虑,因此只有在概要设计完成以后才能准确地得到详细设计或编码阶段的二次WBS,并估计较为准确的整体项目规模。)

逻辑与物理位置:每个对象在逻辑上分别落在哪一层、哪个模块、哪个类;在物理上每个模块、每个对象、每一个类放在哪个应用服务器或客户端的哪个目录、哪个文件(库),或者是建立在数据库管理系统中的什么东东(过程、函数、视图、触发器等等)。

八、结构化与面向对象方法特点比较

1. 从概念方面看,结构化软件是功能的集合,通过模块以及模块和模块之间的分层调用关系实现;面向对象软件是事物的集合,通过对象以及对象和对象之间的通讯联系实现;

2. 从构成方面看,结构化软件=过程+数据,以过程为中心;面向对象软件=(数据+相应操作)的封装,以数据为中心;

3. 从运行控制方面看,结构化软件采用顺序处理方式,由过程驱动控制;面向对象软件采用交互式、并行处理方式,由消息驱动控制;

4. 从开发方面看,结构化方法的工作重点是设计;面向对象方法的工作重点是分析;但是,在结构化方法中,分析阶段和设计阶段采用了不相吻合的表达方式,需要把 在分析阶段采用的具有网络特征的数据流图转换为设计阶段采用的具有分层特征的结构图,在面向对象方法中则不存在这一问题。

5. 从应用方面看,相对而言,结构化方法更加适合数据类型比较简单的数值计算和数据统计管理软件的开发;面向对象方法更加适合大型复杂的人机交互式软件和数据统计管理软件的开发;

参考文献:

《实用软件工程》第二版,郑人杰、殷人昆、陶永雷等著

微软项目:求生法则》Steve McConnell著,余孟学译

《软件工程:实践者的研究方法》(第5版)Roger S.Pressman著

《软件构架实践》SEI软件工程译丛,林·巴斯著

《RUP2000》电子版;

《UML与系统分析设计》张龙祥著;

《面向对象的分析与设计》杨正甫著;

2009年2月2日星期一

在爱思奇想。

这是新的开始,工作、生活,还有博客。

关于我

我的照片
从事计算机软件应用软件开发,4年经验,做过若干个项目,涉及电信、安防、视频等领域。

他/她/它 来过