《Inside C++ Object 》 阅读笔记(1), NRV(Named Return
Value)
write by** 九天雁翎(JTianLing) –
blog.csdn.net/vagrxie**
书其实看完了,因为实在公司抽中午吃饭剩余时间看的,所以其实一直没有好好的试验一下,现在将书带到家里好好的一个一个将感兴趣的测试一下了。
NRV(Named Return Value)是我阅读《Inside C++ Object》碰到的第一个感兴趣的东西,书上面有Lippman的测试数据和侯捷的测试数据。当然,对于VS的效率一直没有报太大希望,但是不至于这个Lippman所说的编译器的义不容辞的优化都不做吧。可能因为侯捷用的VC5.0实在太老了,于是我自己决定测试一下。
首先在Linux下,我测试了一下。测试代码书上有,我还是贴一下:
#include <stdio.h>
#include <stdlib.h>
#include <memory>
#ifdef _WIN32
#include “jtianling.h”
#endif
using namespace std;
class CTest
{
friend CTest foo(double);
public:
CTest()
{
memset(array, 0, 100*sizeof(double));
}
// 以下面的语句决定是否进行NRV
CTest(const CTest&t)
{
memcpy(this, &t, sizeof(CTest));
}
private:
double array[100];
};
CTest foo(double val)
{
CTest local;
local.array[0] = val;
local.array[99] = val;
return local;
}
int main(int argc, char* argv[])
{
#ifdef _WIN32
double timenow = jtianling::GetTime();
#endif
for(int i=0; i<10000000; ++i)
{
CTest t = foo(double(i));
}
#ifdef _WIN32
timenow = jtianling::GetTime() - timenow;
cout <<timenow;
#endif
system("pause");
exit(0);
}
因为在linux下我有sheel下的time可以用,所以不需要自己在程序中计时(实际上这还导致linux下的时间会长一些,因为记上了循环外的部分,包括了main的调用等),windows下用我自己的带有gettime的一个库。
效果是很有意思的,在linux下测试得出不进行NRV优化,进行NRV优化(g++ -O1)和进行NRV优化(g++ -O3)时,在我的P3 800的可怜机器上得出的结果是8.61,5.08,4.67。
在我的windows下用VS 2005 SP1编译,跑在E2160的机子上,debug时,没有拷贝构造,即理论上不进行NRV优化时的时间是5.07,加上拷贝构造函数,即理论上进行NRV时的时间的确和侯捷一致,时间增加到了5.55.可见起码在关闭优化的时候,VS2005没有进行优化。
开启优化呢?更绝得事情来了,VS2005将整个循环优化掉了-_-!作弊!时间虽然短到-7e,但是我也不承认:)
罪证拷贝如下:
00401038 call edi
0040103A fild qword ptr [esp+30h]
0040103E fild qword ptr [___@@_PchSym_@00@UnbLwlxfnvmgUerhfzoLhgfwrlLCAAFUkilqvxghUgvhgxifmgrnvUgvhgkrhzhxrrUivovzhvUhgwzucOlyq@+8 (403390h)]
00401044 fdivp st(1),st
00401046 mov edx,dword ptr [___@@_PchSym_@00@UnbLwlxfnvmgUerhfzoLhgfwrlLCAAFUkilqvxghUgvhgxifmgrnvUgvhgkrhzhxrrUivovzhvUhgwzucOlyq@+8 (403390h)]
0040104C fstp qword ptr [esp+30h]
00401050 or edx,dword ptr [___@@_PchSym_@00@UnbLwlxfnvmgUerhfzoLhgfwrlLCAAFUkilqvxghUgvhgxifmgrnvUgvhgkrhzhxrrUivovzhvUhgwzucOlyq@+0Ch (403394h)]
00401056 jne main+67h (401067h)
00401058 push offset ___@@_PchSym_@00@UnbLwlx
会发现循环完全被抛弃掉了,不过虽然影响了测试,但是这个时候我还得承认这是条还算合理的优化,因为在release时,这个循环不能给我们带来任何我们想要的东西。实际上以前就经常碰到这样的问题,MS老是喜欢将它认为无意义的东西直接优化没有,。。。。常常影响测试。。。。。呵呵,今天我是的确想知道,它到底有没有NRV优化,于是,让这个循环有意义吧。
将程序改成如下状态
#include "jtianling.h"
#include <stdio.h>
#include <stdlib.h>
#include <memory>
using namespace std;
int gi = 1;
int gj = 1;
class CTest
{
friend CTest foo(double);
public:
CTest()
{
gj++;
memset(array, 0, 100*sizeof(double));
}
//CTest(const CTest&t)
//{
// gj++;
// memcpy(this, &t, sizeof(CTest));
//}
double array[100];
};
CTest foo(double val)
{
CTest local;
local.array[0] = val;
local.array[99] = val;
return local;
}
int main(int argc, char* argv[])
{
double timenow = jtianling::GetTime();
for(int i=0; i<10000000; ++i)
{
CTest t = foo(double(i));
gi = t.array[99];
}
timenow = jtianling::GetTime() - timenow;
cout <<timenow <<"/t" <<gi <<"/t" <<gj <<endl;
system("pause");
exit(0);
}
分别测试有copy constructor和没有的情况,结果仍然和以前一致,虽然速度很快,但是,有copy constructor的时候速度还是要慢一些,这点很让人不解,更让人不解的是,既然没有开启NRV,那么应该会有1次default constructor的调用用来构建临时变量,然后再有一次copy constructor来返回值,但是实际上gj显示default constructor和copy constructor中只有一个被调用了。说明起码是进行了某种优化了。
难道MS用的是另外一个不同于NRV的优化方案?
于是我还是从汇编来看,再怎么优化它逃不过我的眼睛:)
00401337 xor esi,esi
00401339 fstp qword ptr [esp+38h]
0040133D add dword ptr [gj (403024h)],989680h
00401347 mov dword ptr [esp+30h],esi
0040134B jmp main+60h (401350h)
0040134D lea ecx,[ecx]
00401350 fild dword ptr [esp+30h]
00401354 call _ftol2_sse (401BE0h)
00401359 add esi,1
0040135C cmp esi,989680h
00401362 mov dword ptr [gi (403020h)],eax
00401367 mov dword ptr [esp+30h],esi
0040136B jl main+60h (401350h)
Have you seen it?红色部分,当我第一次看到989680h的时候,它的值已经是一千万了,说白了就是MS将原来的废循环中的唯一一条不废的语句抽出来,然后在编译期就算好了这条不废语句应该有的值,然后运行时仅仅进行了一条赋值操作。而且绝的是,编译期的这个值的计算是按NRV优化后的流程进行的,即gj为1千万。。。。。。优化成这样,我不知道该怎么说了。。。。。根本就不会有任何的构造函数和拷贝构造函数的调用。
然后我将copy constructor注释掉,再次编译运行,最最让人惊讶的事情发生了,汇编代码完全一样。。。。。不是我眼睛不好使,我用BC对比后结果也是一样的。天哪,这还算优化吗?。。。。简直是恶意篡改代码……..呵呵,说严重了。问题是。。。最大的问题是,无论我测多少次,一样的汇编代码,在有copy constructor的版本总会慢一些,数量级在0.0001上,我也曾怀疑可能是偶尔的机器性能波动,但是实际多次测试,有copy constructor的版本就是会慢一些,希望有人能给我解答这个问题。
write by** 九天雁翎** (JTianLing) –
blog.csdn.net/vagrxie
阅读全文....
漫谈C++中的宏
--接近你的朋友,更接近你的敌人
write by九天雁翎(JTianLing) –
blog.csdn.net/vagrxie**
为什么宏是敌人:
原谅我将C++中的宏称作敌人,自从看到D&E中Bjarne说Cpp(这里指C语言预处理)必须被摧毁后,我就一直尽量避免使用宏,为什么我要去使用一个必须被摧毁的东西?(原谅我脑袋中对名词处理不清,不过“预处理”的主要部分可能就是“宏“)
即便我自己尽量不使用宏了,但是宏依旧给我带来了很多的问题。随便举几个例子,最典型的就是windef.h(包含windows.h也会包含这个文件)中的max,min宏,这两个宏会直接导致C++标准库中的numberlimit使用出现问题,这点我以前已经讲过几次了。上次看到X264的代码前几行就是
的时候我是特别感叹。。。真是害人的东西啊。
还有一个例子,MFC自动生成的文件为了调试方便,所有的new在debug版本下都被宏替换成DEBUG_NEW,实际上被替换成了类似new(void p,const char filename,
int line)这样的形式,为的是跟踪每次的内存分配。这也是为什么MFC程序在出现内存泄露的时候你能在VS output中看到一大堆的memory leak。问题是,因为用了宏。。。带了了很大的副作用。我个人new的时候,有的时候喜欢用new(nothrow)的形式,为的是用老的new,然后判断返回的指针是否为空,这样可以避免使用异常处理,不是我不喜欢catch。。。。公司总监说一般不要用,老是容易出问题。另外,就«inside C++ object»中说,就算一个程序从来不抛出异常,加上一个catch语句都会有3%~5%的效率损失。。。。这也不算是小损失了。。。
最近我同事做游戏音效管理的时候也碰到了一个很难以理解的问题。那就是明明正确PlaySound函数,总是报没有实现的编译错误。。。。。最后的问题竟然还是宏的问题。windows下经典的用来实现ansi/unicode通用的方式就是用宏大家都知道吧,因为windowsAPI中也有PlaySound函数,所以我同事程序中因为一边包含了windows的东西,一边没有包含,结果就是一边的PlaySound被替换成PlaySoundW了,而调用的PlaySound就不存在了。。。。这样的问题,真的让人吐血。。。。。。。
宏的无类型检测和随意性导致任何一个本来正确的程序都可以变成错误的。这也就是为什么Bjarne认为它必须被毁灭。这也是为什么我要将宏视为敌人。。。。
接近你的朋友:
宏在C中的这么几个作用在C++中有相应的替代品,并且推荐使用。
1. 用#define来定义常量
可以用全局的const变量来代替,永远要知道#define是不带类型检查的,而const变量带。虽然宏的使用可能更节省了一个全局变量,但是除非特殊需求,不值得为这样一个全局变量去牺牲安全性。
2. 用#define来定义函数并且使函数扩展,减少运行时消耗
可以用inline函数替换,inline函数带参数类型检查,并且不会无端的将你正常的程序变得不正常(参考上面的例子)
3. 用#define来实现类型无关的容器
在没有template之前,这是标准的方法,在有template之后。。。这是废弃的方法。
推荐的方法是你的朋友,宏是你的敌人。
更接近你的敌人
首先顺便说说其他的预处理:
1.#include用来包含文件,这点的作用至今无法替代,大家用的也都没有什么问题。在Bjarne引入include关键字之前,我们只能用它。
2.#ifdef
#define
#endif
组合用来防止头文件的重复包含,这个使用C++也没有提供替代方案。也是无法替代。(VS中好像#progra once可以)虽然Bjarne说这是很拙劣很低效的方法,但是在我们没有找到更合理的方式前,也就只能这样使用了。
3.用#ifdef #elif #else
#endif来实现条件编译,这是防止头文件重复包含的泛化版本,这在C++中也没有办法替代。为什么MS只能用这种方式来实现一套源码ANSI/Unicode通用?因为没有其他办法。实际上,用一套源码来维护的可移植性代码通篇的#ifdef #elif #else
#endif那是太正常的事情了。哪怕像因为哦我们公司将debug版本的程序加上_dbg的后缀这样的工程管理方式,都会导致所有希望以CreateProcess方式调用此程序的程序都加上条件编译。常用的用来实现条件编译的宏有_DEBUG,_RELEASE,_WIN32,_WINVER,_LINUX等。。。。。。。。。
4. LINE,FILE__其实好像一般人都用不着,但是这简直就是调试者的福音,因为你可以简单的将malloc,new等宏扩展成new(size,__LINE,FILE)这样的调用形式,再自己实现之,呵呵,其实这也就是MFC内存管理的原理。另外,我做内存管理模块的时候才知道,原来malloc其实就是一个宏定义,-_-!就我所知,可能是因为在C语言时代,没有重载这一说,但是考虑到总会有人想来自己管理内存,所以才出此一招。。。。
下面是重头戏,宏,#define
除了定义常量,函数等基础功能能够被C++中的某些特性替代以外,更多的特性无法替代。#define就是一把达摩克利斯之剑。除了程序代码我不知道该怎么形容#define可以带来的东西:)几乎可以做到do anything you want? 当和条件编译结合使用的时候,你会发现你几乎就是上帝:)
1.)最简单的情况,节省你敲代码的时间:)
很多时候一个深层的调用是这样子的mpObject->moMap.first->SomeFun(),
你一个#define mpObject->moMap.first->SomeFun CallSomeFun
以后所有这样的复杂调用就简单多了:)
2.)次最简单的情况,替代函数
很多时候人们说不要让重复的代码出现多次,都是告诉你可以将重复的代码做成函数。但是有的时候几个平行的代码,做成函数不方便的时候,宏就更加适合。举个例子:
++SomeLocalA;
++ SomeLocalB;
++ SomeLocalC;
++ SomeLocalD;
++ SomeLocalE;
当类似这样代码重复出现的时候,确定要用函数吗?后果是,函数要定义成
Increment(int
&a,int &b,int &c, int&d, int&e)
形式,调用起来还得
Increment(SomeLocalA,
SomeLocalB, SomeLocalC, SomeLocalD, SomeLocalE);
你认为任务轻松了?。。。。。。。
用#define吧。。。。。。
#define INCREMENT
++SomeLocalA;/
++ SomeLocalB;/
++ SomeLocalC;/
++ SomeLocalD;/
++ SomeLocalE;
后,在次使用只需要输入INCREMENT就行了,这才叫简化。
3.)次次最简单的应用,版本控制和平台无关性控制。
这也算是一种标准的用法了,特别是库用的特别多,一般和条件编译结合使用,很多常见的宏比如_WINVER就是属于版本控制,_WIN32,_LINUX就是属于平台控制。参考预定义介绍3.
4.)简单的应用,方便的调试。。。。
某时也可以和#ifdef综合应用,参看预定义介绍4.
还是举例说明吧,比如断言这样最经典的东西大家都知道吧,assert啊。。。程序中往往插入了成百上千的assert,有一天你不想要了怎么办?编译成release版本?晕,调试呢?简单的解决办法是这样
然后你平时使用断言的时候全部用ASSERT,某一天你不想使用了,很简单的一句
然后断言全部取消了。
这一点的应用是很广泛的,比如日志系统在程序版本还不是很稳定的时候是很需要的,当程序已经比较稳定的时候还频繁的输出日志,那就会影响程序的效率,那怎么办?你一条一条将原来的日志删除?OK,你很聪明,我用全局替换成空的。。。。。。呵呵,假设你会用正则表达式是可以做到的:)随便说一句,我很喜欢用:)那么有一天程序出问题,又希望加日志呢?又一条一条加?假如会正则表达式的话还是有解决方案,呵呵,将原有的所有日志前面全部加上//,需要的时候再去掉//就行了。方法倒是可行(说实话可行),就是当工程大的时候,消耗的时间啊。。。。。
其实还是用#define最简单
用来输出日志,然后某天你不用了
就没有了。。。。。
这点还有个特别的好处:)比如你想在所有的日志前面加上个前缀:)
这时你改变你的LOG_OUT宏就可以了,不用全局去改。
加上__FILE__,__LINE__后对于debug和详细日志输出的帮助是无限大的。当同一个函数在不同的情况下被调用的时候,你希望知道是谁调用了这条函数,你能怎么做?直接的办法是,你改变原有的函数,添加进一个行参数两个新的字符串参数,并且定义你扩展的函数。其实可以直接将原有函数用宏替换走:)这也就是上面提到的new的替换方式,可能带来副作用,但是好用至极。这里不举其他例子了,可以参考MFC 的DEBUG_NEW的宏。
4b)这点有个延生用法,那就是当某个新功能你需要添加进原有的程序,但是你需要先测试和保证不影响原有的程序,你将此功能的代码做成一条宏语句是最简单的方法,不仅节省了你copy大量代码的时间,还能在你不需要的时候直接取消此功能。这种用法也是很广泛的,而且特别实用。
这一点的例子可以参看MFC中定义的FIX_SIZE宏,我将它们贴出来,呵呵,想到侯捷可以将所有的源代码都拿出来剖析,我随便贴一点应该不违法吧-_-!
//
DECLARE_FIXED_ALLOC -- used in class definition
#define DECLARE_FIXED_ALLOC(class_name) /
public: /
void* operator
new(size_t size) /
{ /
ASSERT(size == s_alloc.GetAllocSize()); /
UNUSED(size); /
return s_alloc.Alloc(); /
} /
void* operator
new(size_t, void* p) /
{ return p; } /
void operator
delete(void* p) { s_alloc.Free(p); } /
void* operator
new(size_t size, LPCSTR, int) /
{ /
ASSERT(size == s_alloc.GetAllocSize()); /
UNUSED(size); /
return s_alloc.Alloc(); /
} /
protected: /
static CFixedAlloc
s_alloc /
//
IMPLEMENT_FIXED_ALLOC -- used in class implementation file
#define IMPLEMENT_FIXED_ALLOC(class_name, block_size)
/
CFixedAlloc class_name::s_alloc(sizeof(class_name), block_size)
/
//
DECLARE_FIXED_ALLOC -- used in class definition
#define DECLARE_FIXED_ALLOC_NOSYNC(class_name) /
public: /
void* operator
new(size_t size) /
{ /
ASSERT(size == s_alloc.GetAllocSize()); /
UNUSED(size); /
return s_alloc.Alloc(); /
} /
void* operator
new(size_t, void* p) /
{ return p; } /
void operator
delete(void* p) { s_alloc.Free(p); } /
void* operator
new(size_t size, LPCSTR, int) /
{ /
ASSERT(size == s_alloc.GetAllocSize()); /
UNUSED(size); /
return s_alloc.Alloc(); /
} /
protected: /
static CFixedAllocNoSync
s_alloc /
//
IMPLEMENT_FIXED_ALLOC_NOSYNC -- used in class implementation file
#define IMPLEMENT_FIXED_ALLOC_NOSYNC(class_nbame, block_size)
/
CFixedAllocNoSync class_name::s_alloc(sizeof(class_name), block_size)
/
#else //!_DEBUG
#define DECLARE_FIXED_ALLOC(class_name) // nothing
in debug
#define IMPLEMENT_FIXED_ALLOC(class_name, block_size)
// nothing in debug
#define DECLARE_FIXED_ALLOC_NOSYNC(class_name) // nothing
in debug
#define IMPLEMENT_FIXED_ALLOC_NOSYNC(class_name, block_size)
// nothing in debug
#endif //!_DEBUG
上边的宏定义出自fixalloc.h文件,详细的意义可以到MFC的源代码中去查,这样的话只要在需要FIXALLOC的地方使用DECLARE_FIXED_ALLOC和IMPLEMENT_FIXED_ALLOC宏就可以实现功能了,并且可以很方便的控制,比如上边的代码就让宏在relase版本下才有用,在debug版本下放空了。
5.次次复杂应用,自动扩展甚于模板的函数。
这时候需要一个很重要的特性“##” :)使用宏定义以后的连接功能。比如我做数据校验器的时候用到n个Init。。。和n个CxxxCheck类,但是名字都是不一样的,做成模板似乎很适合,其实做不到。因为你甚至没有办法来确定哪个类用哪个mbxxxInit来表示已经初始化的问题。但是宏可以。模板往往也不能详细的分辨出每次调用的具体类型并输出日志,宏也可以。
比如,
#define Init(_CHECK) if(mp##_CHECK->Init())/
{/
mb##_CHECK = true;/
}
else
{/
LOG_OUT("Init" <<#_CHECK
<<"failed" <<endl);/
}
当然,这有个局限,就是你得将命名规范化,还好这不是什么难事。
6.次复杂应用,大家都知道的,消息映射。
其实消息映射可以理解为5b的衍生,但是实际上用途更加强大,MFC的消息映射大家都知道吧:)其实消息映射可以应用到很多方面,除了MFC的例子我多说了,在网络包的分派上应用也是很经常的,起码我就常常用,虽然这个时候使用的方法可能应该不能叫消息映射了,叫包分派更加合适。其实更深一步说,在牵涉到一个一个固定入口值,然后分派到不同的函数的过程,假如数量多的话,都可以使用消息映射的模式,这样最节省代码,也可以省下很多虚接口。唯一的一点是,定好命名规则。。。不要打错字。。。
7.复杂应用,工厂模式。
虽然C++中可以不用宏实现工厂模式。但是使用的时候用到宏会方便太多。。。。好像举例子比较复杂。。。。可以参考“四人帮”的书Gof23
比如一个CreateClass(classname)的函数,创建并返回一个你指定的类。你会怎么做?
if (classname == “classa”)
return new CClassA else if (classname == “classb”) return new CClassB else
if........................
别的不说,在我们公司的游戏界面库中的类都有40多个。。。。也就是说,你要创建一个Class40..需要比较40多次。。。。。。效率自然低了。这其实是我在公司解决的一个问题。。。。呵呵,做了很久了,当然我不能将公司的源码贴出来给大家看:)不过CEGUI的例子倒是可以:)
基本原理就是先将所有的可能创建的类的都定义一个对应的工厂类,此类中包含一个成员函数new并返回这个类的一个对象。要生成这个类的时候就找到对应的工厂并调用这个生成函数,实际使用的时候利用##的连接特性,自己拼出需要生成类,然后调用。另外类工厂通常做成单件。以下是代码,呵呵,别的就不多说了,源码能说明问题。想详细看的,可以参考CEGUI源代码的CEGUIWindowFactory.h文件,这个用法其实也和6一样,需要命名规范。说了半天,其实做到的效果就是通过一个字符串来动态创建一个对应的类。在CEGUI中,控件几乎都是通过这种方式来创建的,这样再和XML解析配合,可以达到极大的自由度。这里所谓的自由是指可以达到不修改程序,光修改XML的配置文件,就可以实际修改程序(游戏)的效果。呵呵,扯远了。
#define CEGUI_DECLARE_WINDOW_FACTORY(
T )/
class T ## Factory : public WindowFactory/
{/
public:/
T ##
Factory() : WindowFactory(
T::WidgetTypeName
) {}/
Window* createWindow(const
String& name)/
{/
return new T (d_type, name);/
}/
void destroyWindow(Window*
window)/
{/
delete window;/
}/
};/
T ##
Factory& get
## T ## Factory();
/*!
/brief
Generates code for the constructor for the
instance of the window factory generated
from the class name /a T
*/
//
#define CEGUI_DEFINE_WINDOW_FACTORY(
T )/
T ##
Factory& get
## T ## Factory()/
{/
static T
## Factory s_factory;/
return s_factory;/
}
/*!
/brief
Helper macro that return the real factory
class name from a given class name /a T
*/
#define CEGUI_WINDOW_FACTORY(
T ) (get ## T ## Factory())
write by九天雁翎(JTianLing) –
blog.csdn.net/vagrxie
阅读全文....
个人研究《数据结构与算法分析-C++描述》Vector实现的问题,new与初始化
write by** 九天雁翎(JTianLing) –
blog.csdn.net/vagrxie**
«Data Structures and Algorithm Analysis in C++»
--《数据结构与算法分析c++描述》 Mark Allen Weiss著 人民邮电大学出版 中文版第61面, 图3-7,3-8,实现的一个列表Vector类。
原实现大概如下:(我可能修改了一些变量的命名以符合我的习惯)
template<typename T>
class Vector
{
public:
explicit Vector(unsigned auInitSize
= 0)
: muSize(auInitSize),muCapacity(auInitSize \+ SPARE_CAPACITY)
{
mpObjects = new T[muCapacity];
}
Vector(const Vector &aoObject):mpObjects(0)
{
*this = aoObject;
}
~Vector()
{
delete[] mpObjects;
}
const Vector&
operator= (const
Vector &aoObject)
{
if(this
== &aoObject)
{
return *this;
}
delete[] mpObjects;
muSize = aoObject.size();
muCapacity = aoObject.capacity();
mpObjects = new T[ capacity() ];
for(unsigned
i=0; i!=muSize; ++i)
{
mpObjects[i] = aoObject[i];
}
return *this;
}
void resize(unsigned auNewSize)
{
if ( auNewSize
> muCapacity )
{
reserve(auNewSize * 2 + 1);
}
}
void reserve(unsigned auNewCapacity)
{
if(auNewCapacity
< muSize)
{
return;
}
T *lpOldArray
= mpObjects;
mpObjects = new T[auNewCapacity];
for(unsigned
i=0; i!=muSize; ++i)
{
mpObjects[i] = lpOldArray[i];
}
muCapacity = auNewCapacity;
delete[] lpOldArray;
}
T& operator[](unsigned auIndex)
{
return mpObjects[auIndex];
}
const T&
operator[](unsigned
auIndex) const
{
return mpObjects[auIndex];
}
bool empty()
const
{
return (muSize == 0);
}
unsigned size()
const
{
return muSize;
}
unsigned capacity()
const
{
return muCapacity;
}
void push_back(const T& aoObject)
{
if(muSize
== muCapacity)
{
reserve(2 * muCapacity \+ 1);
}
mpObjects[muSize++] = aoObject;
}
void pop_back()
{
muSize\--;
}
const T&
back() const
{
return mpObjects[muSize-1];
}
typedef T*
iterator;
typedef const
T* const_iterator;
iterator begin()
{
return mpObjects;
}
const_iterator begin() const
{
return mpObjects;
}
iterator end()
{
return mpObjects \+ muSize;
}
const_iterator end() const
{
return mpObjects \+ muSize;
}
enum { SPARE_CAPACITY
= 16 };
private:
unsigned muSize;
unsigned muCapacity;
T *mpObjects;
};
首先的第一个感觉就是new了以后没有初始化,而且放任其未初始化的值的使用。(说的这么复杂,就是说使用了没有初始化的值贝)
但是实际我在Linux下测试的时候发现,g++是会自动将所有new出来的内存初始化的,事实就是如此,这和我在windows下的常识有冲突,所以特意做了一个测试程序来实验。
int main( void )
{
int *lpi
= new int[100];
for(int
i=0; i<100;
++i)
{
printf("%d",lpi[i]);
}
return 0;
}
在linux下总是会输出全0,哪怕我怀疑碰巧这段内存没有被使用过,将100变成1000也是全0.只能承认,new以后是自动初始化的了。
我甚至怀疑我的常识是否是错误的(虽然我以前为了保证初始化,认为每次new完以后,哪怕马上要全部的使用这些内存都会先用memset置空一下是个好的习惯)
事实上,在VS2005的测试中,结果我的常识一样,在debug版本下,windows会自动置为0xcdcdcdcd等值,release时为随机值。
我只能说,以前我一直没有注意到g++编译下自动初始化的事实。
然后我特意看了一下VS和libstdc++的vector实现,在resize(size)的时候都是T()作为第二参数调用重载的另一个函数resize(size,val)来实现的,也就是说,C++标准库的这两个实现还是确保reseize初始化了。
ok,也许Mark Allen Weiss的Vector在Linux下可以不出错。。。但是我还是认为这个程序写的有问题,加上初始化吧。。。。。。
自己Mark一下,原来g++编译的new带初始化的?希望有高人能够给我解答,假如不是g++编译的new带初始化那么是什么情况导致了这样的情况,还有,g++有关掉初始化的选项吗?
write by** 九天雁翎** (JTianLing) – www.jtianling.com
阅读全文....
一个无聊男人的疯狂《数据结构与算法分析-C++描述》学习笔记
用C++/lua/python/bash的四重实现(7)习题2.8 随机数组的三种生成算法
write by** 九天雁翎(JTianLing) –
blog.csdn.net/vagrxie**
«Data Structures and Algorithm Analysis in C++»
--《数据结构与算法分析c++描述》 Mark Allen Weiss著 人民邮电大学出版 中文版第49面, 一随机数组的三种生成算法
这一次没有截图,因为我受不了让bash运行100次的速度,怎么说来着,还是那句话,Bash根本就不适合也不是设计用来描述算法的语言,我根本就是自讨苦吃。用了最多的时间,得到的是最低的效率。。。。。。还有最扭曲的代码。但是因为工作中不用到bash,不常用用还真怕都忘了。。。现在用python的时候就常有这样的感觉,以前看的书就像白看了一样。
相对于bash来说,python的优雅一次一次的深入我心,每一次它都是代码最短的,最最优雅的,很多便捷的语法都是让人用的很痛快,虽然刚开始用的时候由于没有大括号。。。(我用c++习惯了)感觉代码就像悬在空中一样的不可靠。。。呵呵,习惯了以后,怎么看怎么爽。再加上丰富的库,绝了。
write by 九天雁翎(JTianLing)
-- www.jtianling.com
以下为实现部分:
CPP:
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <iterator>
#include <iostream>
using namespace std;
int randInt(int i, int j)
{
if(i > j)
{
int temp = i;
i = j;
j = temp;
}
return rand() % (j-i) + i;
}
void randArr1(int arr[], int n)
{
for(int i=0; i<n; ++i)
{
while(true)
{
// some thing don't like py
// because my randInt is create number
// in [0,n) and py is [0,n]
int temp = randInt(0, n);
int j = 0;
while(j < i)
{
if(arr[j] == temp)
{
break ;
}
++j;
}
if(j == i)
{
arr[j] = temp;
break ;
}
}
}
}
void randArr2(int a[], int n)
{
bool *lpb = new bool[n];
memset(lpb, 0, n);
for(int i=0; i<n; ++i)
{
while(true)
{
int temp = randInt(0, n);
if(!lpb[temp])
{
a[i] = temp;
lpb[temp] = true;
break ;
}
}
}
delete lpb;
lpb = NULL;
}
void swap(int & a, int & b)
{
int temp = a;
a = b;
b = temp;
}
// just for fun,but I like it.
// when py could have range and bash could have seq
// why cpp couldn't have a seq like this? lol
template <typename T>
class CFBSeq
{
public :
CFBSeq(const T& aValue) : mValue(aValue) { }
T operator()()
{
return mValue++;
}
private :
T mValue;
};
void randArr3(int a[], int n)
{
generate(a, a+n, CFBSeq<int >(0));
for(int i=1; i<n; ++i)
{
swap(a[i], a[randInt(0,i)]);
}
}
int main(int argc, char * argv[])
{
const int TIMES=100;
srand(time(NULL));
int a[TIMES],b[TIMES],c[TIMES];
randArr1(a,TIMES);
copy(a, a+TIMES, ostream_iterator<int >(cout," "));
printf("/n");
randArr2(b,TIMES);
copy(b, b+TIMES, ostream_iterator<int >(cout," "));
printf("/n");
randArr3(c, TIMES);
copy(c, c+TIMES, ostream_iterator<int >(cout," "));
exit(0);
}
LUA:
#!/usr/bin/env lua
math.randomseed(os.time())
function randArr1(a, n)
for i=1,n do
while true do
local temp = math.random(1, n)
local j = 1
while j < i do
if a[j] == temp then
break
end
-- Again damed that There is no ++
-- and even no composite operator += !
-- No one knows that is more concision
-- and more effective?
j = j + 1
end
if j == i then
a[i] = temp
break
end
end
end
end
function randArr2(a, n)
-- use nil as false as a lua's special hack
local bt = {}
for i=1,n do
while true do
local temp = math.random(1,n)
if not bt[temp] then
a[i] = temp
bt[temp] = true
break
end
end
end
end
function randArr3(a, n)
for i=1,n do
a[i] = i
end
for i=1,n do
local temp = math.random(1,n)
-- one of the most things i like in lua&py
a[i],a[temp] = a[temp],a[i]
end
end
-- Test Code
times = 100
t = {}
randArr1(t, times)
for i,v in ipairs(t) do
io.write(v .. " ")
end
t2 = {}
randArr2(t2, times)
for i,v in ipairs(t2) do
io.write(v .. " ")
end
t3 = {}
randArr3(t3, times)
for i,v in ipairs(t3) do
io.write(v .. " ")
end
PYTHON:
#!/usr/bin/env python
from random import randint
def randArr1(a,n):
for i in range(n):
while True:
temp = randint(0, n-1)
for j in range(i):
if a[j] == temp:
break ;
# another one of the things I like in py(for else)
else :
a.append(temp)
break ;
def randArr2(a,n):
#surely it is not need be a dict but dict is faster then list
bd = {}
for i in range(n):
while True:
temp = randint(0, n-1)
if temp not in tl:
a.append(temp)
tl[temp] = True
break
def randArr3(a,n):
a = range(n)
for i in range(n):
temp = randint(0, n-1)
# like in lua
a[i],a[temp] = a[temp],a[i]
def test():
times = 100
l = []
randArr1(l, times)
print l
l2 = []
randArr1(l2, times)
print l2
l2 = []
randArr1(l2, times)
print l2
if __name__ == '__main__':
test()
BASH:
#!/usr/bin/env bash
# just for a range rand....let me die....
# what I had said? bash is not a good
# language for describing algorithms
randInt()
{
local a=$1
local b=$2
if (( a > b ))
then
local temp
(( temp = a ))
(( a = b ))
(( b = temp ))
fi
local mi
(( mi = b - a + 1 ))
local r=$((RANDOM%${mi}+${a}))
echo -n $r
}
randArr1()
{
# only one argument because I hate the
# bash's indirect reference
# you can reference the (4) binarySearch to
# see what I said
local n=$1
declare -a a
for (( i=1; i<=n; ++i ))
do
while true
do
temp=`randInt 1 $n`
j=1
while (( j < i ))
do
if (( a[j] == temp ))
then
break
fi
(( ++j ))
done
if (( j == i ))
then
(( a[ j ] = temp ))
break
fi
done
done
echo ${a[*]}
}
randArr2()
{
local n=$1
declare -a a
# used for bool array
declare -a b
for (( i=1; i<=n; ++i ))
do
while true
do
local temp=`randInt 1 $n`
if [ -z ${b[temp]} ]
then
(( a[ i ] = temp ))
(( b[ temp ] = true ))
break
fi
done
done
echo ${a[*]}
}
randArr3()
{
local n=$1
for (( i=1; i<=n; ++i ))
do
(( a[ i ] = i ))
done
for (( i=1; i<=n; ++i ))
do
local temp=`randInt 1 $n`
(( t = a[ i ] ))
(( a[ i ] = a[ temp ] ))
(( a[ temp ] = t ))
done
echo ${a[*]}
}
# so slow that I can't bear it run 100 times
randArr1 10
randArr2 10
randArr3 10
write by** 九天雁翎** (JTianLing) – www.jtianling.com
阅读全文....
一个无聊男人的疯狂《数据结构与算法分析-C++描述》学习笔记
用(7)习题2.8 随机数组的三种生成算法补 将bash的实现翻译成比较纯正的bash风格
write by** 九天雁翎(JTianLing) –
blog.csdn.net/vagrxie**
«Data Structures and Algorithm Analysis in C++»
--《数据结构与算法分析c++描述》 Mark Allen Weiss著 人民邮电大学出版 中文版第49面, 一随机数组的三种生成算法
由于偷懒我最近老是喜欢用bash从C语言(其实好像是csh)中引进的语法,所以感觉都不是那么纯粹的bash风格(但是其实bash的风格我本来就不 喜欢),老是学了不用,有一天写bash都当C语言在写就完了。今天加班比较晚,就不搞什么新内容了,将2.8的bash实现翻译成了比较纯粹的bash 风格。。。。对于没有接触过Bash的人来说更加接近天书了,enjoy it……(I hope you can)
1 #!/usr/bin/env bash
2
3 # just for
a range rand....let me die....
4 # what I had said? bash is not a
good
5 # language
for describing algorithms
6 randInt**()**
7 {
8 **local** a=$1
9 **local** b=$2
10
11 **if** **[** **"** $a**"** **-gt** **"** $b**"** **]**
12 **then**
13 **local** temp=$a
14 a=$b
15 b=$temp
16 **fi**
17
18 **local** mi=$(($b-$a+1))
19 **local** **r=** $((RANDOM%${mi}+${a}))
20 **echo** -n $r
21 }
22
23 randArr1**()**
24 {
25 # only one
argument because I hate the
26 # bash's
indirect reference
27 # you can
reference the (4) binarySearch to
28 # see what I
said
29 **local** n=$1
30 **for** i **in** `seq $n`
31 **do**
32 **while true**
33 ** do**
34 temp=`randInt 1 $n`
35 j=1
36 **while [** **"** $j**"** **-lt** **"** $i**"** **]**
37 ** do**
38 **if** **[** ${a[j]} **-eq** **"** $temp**"** **]**
39 **then**
40 **break**
41 **fi**
42 j=$(($j+1))
43 **done**
44 **if** **[** **"** $j**"** **-eq** **"** $i**"** **]**
45 **then**
46 a**[** j**]=** $temp
47 **break**
48 **fi**
49 **done**
50 **done**
51
52 **echo** ${a[*]}
53 }
54
55 randArr2**()**
56 {
57 **local** n=$1
58 # used for bool array
59 **for** i **in** `seq $n`
60 **do**
61 **while true**
62 ** do**
63 **local** temp=`randInt 1 $n`
64 **if** **[** **-z** ${b[temp]} **]**
65 **then**
66 a**[** i**]=** $temp
67 b**[** temp**]=** true
68 **break**
69 **fi**
70 **done**
71 **done**
72
73 **echo** ${a[*]}
74 }
75
76 randArr3**()**
77 {
78 **local** n=$1
79 **for** i **in** `seq $n`
80 **do**
81 a**[** i**]=** $i
82 **done**
83 **for** i **in** `seq $n`
84 **do**
85 **local** temp=`randInt 1 $n`
86 t=${a[i]}
87 a**[** i**]=** ${a[temp]}
88 a**[** temp**]=** $t
89 **done**
90
91 **echo** ${a[*]}
92 }
93
94 # so slow that I
can't bear it run 100 times
95 randArr1 10
96 randArr2 10
97 randArr3 10
98
write by** 九天雁翎** (JTianLing) – www.jtianling.com
阅读全文....
一个无聊男人的疯狂《数据结构与算法分析-C++描述》学习笔记
用C++/lua/python/bash的四重实现(6)高效率的幂运算
write by** 九天雁翎(JTianLing) –
blog.csdn.net/vagrxie**
«Data Structures and Algorithm Analysis in C++»
--《数据结构与算法分析c++描述》 Mark Allen Weiss著 人民邮电大学出版 中文版第46面,图2-11
一个高效率的幂运算,应该还算不上最高效率的,但是作为一个递归及O(logn)的例子还是很合适的,因为足够的简单。
bash的实现我发现老是用普通程序语言的思想是不行了,麻烦的要死,于是用命令替换实现了一个b版本,明显效果好的多:)用一个语言就是要用一个语言的特性。
来个截图,惯例。我好像喜欢上了这种一下排4,5个窗口然后照相的方式,呵呵,特有成就感。
从左自右依次是cpp,lua,python,bash,bash(b实现)。

write by 九天雁翎(JTianLing)
-- www.jtianling.com
以下为实现部分:
CPP:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
**bool** IsEven(**int** x)
{
**return** (x % 2 == 0);
}
**long** MyPow(**long** x, **int** n)
{
**if**( 0 == n)
**return** 1;
**if** ( IsEven(n))
**return** MyPow( x * x, n / 2);
**else**
**return** MyPow( x * x, n / 2 ) * x;
}
**int** main(**int** argc, **char** * argv[])
{
printf("pow(2,2)=%ld/n",MyPow(2,2));
printf("pow(2,3)=%ld/n",MyPow(2,3));
printf("pow(2,4)=%ld/n",MyPow(2,4));
printf("pow(2,5)=%ld/n",MyPow(2,5));
printf("pow(2,6)=%ld/n",MyPow(2,6));
exit(0);
}
LUA:
#!/usr/bin/env lua
function isEven(n)
**return** n % 2 == 0
end
function pow(m, n)
**if** n == 0 **then**
**return** 1
**end**
**if** isEven(n) **then**
**return** pow( m * m, n / 2)
**else**
**return** pow( m * m, math.floor(n / 2) ) * m
**end**
end
-- Test Code
print("pow(2,2)=" .. pow(2,2));
print("pow(2,3)=" .. pow(2,3));
print("pow(2,4)=" .. pow(2,4));
print("pow(2,5)=" .. pow(2,5));
print("pow(2,6)=" .. pow(2,6));
PYTHON:
#!/usr/bin/env python
**def** IsEven(n):
**return** n % 2 == 0
**def** MyPow(m,n):
**if** n == 0:
**return** 1
**if** IsEven(n):
**return** MyPow(m * m, n / 2)
**else** :
**return** MyPow(m * m, n // 2) * m
**def** test():
**print** "pow(2,2)=" + str(MyPow(2,2))
**print** "pow(2,3)=" + str(MyPow(2,3))
**print** "pow(2,4)=" + str(MyPow(2,4))
**print** "pow(2,5)=" + str(MyPow(2,5))
**print** "pow(2,6)=" + str(MyPow(2,6))
**if** __name__ == '__main__':
test()
BASH:
#!/usr/bin/env bash
isEven**()**
{
**local** m=$1
**if** (( m % 2 **==** 0))
**then**
**return** 1
**else**
**return** 0
**fi**
}
pow**()**
{
**local** m=$1
**local** n=$2
**if** (( n **==** 0 ))
**then**
**return** 1
**fi**
**local** m2
**local** n2
**((** m2 **=** m * m **))**
**((** n2 **=** n / 2 **))**
isEven n
**r=** $?
**if** (( r **==** 1 ))
**then**
pow $m2 $n2
**return** $?
**else**
pow $m2 $n2
**local** r2=$?
(( r2 * **=** m ))
**return** $r2
**fi**
}
**echo** -n **"** pow(2,2)=**"**
pow 2 2
**echo** $?
**echo** -n **"** pow(2,3)=**"**
pow 2 3
**echo** $?
**echo** -n **"** pow(2,4)=**"**
pow 2 4
**echo** $?
**echo** -n **"** pow(2,5)=**"**
pow 2 5
**echo** $?
**echo** -n **"** pow(2,6)=**"**
pow 2 6
**echo** $?
BASH(b实现):
#!/usr/bin/env bash
isEven**()**
{
**local** m=$1
**if** (( m % 2 **==** 0))
**then**
**echo** 1
**else**
**echo** 0
**fi**
}
pow**()**
{
**local** m=$1
**local** n=$2
**if** (( n **==** 0 ))
**then**
**echo** 1
**exit** 0
**fi**
**local** m2
**local** n2
**((** m2 **=** m * m **))**
**((** n2 **=** n / 2 **))**
**r=** `isEven $n`
**if** (( r **==** 1 ))
**then**
**local** r1=`pow $m2 $n2`
**echo** $r1
**else**
**local** r2=`pow $m2 $n2`
(( r2 * **=** m ))
**echo** $r2
**fi**
}
**echo** -n **"** pow 2 2 = **"**
pow 2 2
**echo** -n **"** pow 2 3 = **"**
pow 2 3
**echo** -n **"** pow 2 4 = **"**
pow 2 4
**echo** -n **"** pow 2 5 = **"**
pow 2 5
**echo** -n **"** pow 2 6 = **"**
pow 2 6
write by** 九天雁翎** (JTianLing) – www.jtianling.com
阅读全文....
一个无聊男人的疯狂《数据结构与算法分析-C++描述》学习笔记
用C++/lua/python/bash的四重实现(5)欧几里得算法求最大公约数
write by** 九天雁翎(JTianLing) –
blog.csdn.net/vagrxie**
«Data Structures and Algorithm Analysis in C++»
--《数据结构与算法分析c++描述》 Mark Allen Weiss著 人民邮电大学出版 中文版第45面,图2-10欧几里得算法
欧几里得算法求最大公约数好像也比较出名,起码各大算法书籍都喜欢用,可能因为说明简单,却能够说明问题。
这里突然想说一句,当bash老是用C语法部分来完成也就没有那么难的变态了,只是,可能多了一点投机的成分。
再来个截图,下次再截就成惯例了。
从左自右依次是cpp,lua,python,bash。


write by 九天雁翎(JTianLing)
-- www.jtianling.com
以下为实现部分:
CPP:
#include <stdio.h>
#include <stdlib.h>
**long** gcd(**long** m, **long** n)
{
**if**(m < n)
{
**long** temp = m;
m = n;
n = temp;
}
**while**( n != 0)
{
**long** rem = m % n;
m = n;
n = rem;
}
**return** m;
}
**int** main(**int** argc, **char** * argv[])
{
printf("gcd(1989,1590)=%ld/n",gcd(1989,1590));
printf("gcd(1590,1989)=%ld/n",gcd(1590,1989));
exit(0);
}
LUA:
#!/usr/bin/env lua
function gcd(m,n)
**if** m < n **then**
m,n = n,m
**end**
**while** n ~= 0 **do**
rem = m % n
m = n
n = rem
**end**
**return** m
end
-- Test code
print("gcd(1989,1590)=" .. gcd(1989,1590))
print("gcd(1590,1989)=" .. gcd(1590,1989))
PYTHON:
#!/usr/bin/env python
**def** gcd(m,n):
**if** m < n:
m,n = n,m
**while** n != 0:
rem = m % n
m = n
n = rem
**return** m
**def** test():
**print** "gcd(1989,1590)=" + str(gcd(1989,1590))
**print** "gcd(1590,1989)=" + str(gcd(1590,1989))
**if** __name__ == '__main__':
test()
BASH:
#!/usr/bin/env bash
gcd()
{
m=$1
n=$2
**if** (( m < n ))
then
(( temp = m ))
(( m = n ))
(( n = temp ))
fi
**while **(( n != 0 ))
do
(( rem = m % n ))
(( m = n ))
(( n = rem ))
done
**return** $m
}
# test code
**echo** -n " gcd(1989,1590)="
gcd 1989 1590
**echo** $?
**echo** -n " gcd(1590,1989)="
gcd 1590 1989
**echo** $?
write by** 九天雁翎** (JTianLing) – www.jtianling.com
阅读全文....
一个无聊男人的疯狂《数据结构与算法分析-C++描述》学习笔记 用C++/lua/python/bash的四重实现(4)二分搜索算法
write by** 九天雁翎(JTianLing) –
blog.csdn.net/vagrxie**
«Data Structures and Algorithm Analysis in C++»
--《数据结构与算法分析c++描述》 Mark Allen Weiss著 人民邮电大学出版 中文版第44面,图2-9二分搜索算法
二分搜索是很著名也很实用的算法,在排序中查找的速度从算法分析角度来说已经是最快的了。O(logN)
这里费了点心,将bash都实现了,扭曲啊,扭曲,光是传递一个数组参数都折腾了半天,最后还是通过所谓的间接引用然后用for重新赋值才实现我想要的数组,再次认为bash的算法描述语法混乱不堪。。。。其实bash只有命令调用的语法好用而已(个人见解),但是碰到我这样无聊的人,偏偏要用bash去实现算法。。。那就只能稍微偷点懒了,用了bash的C语言语法部分。
以下4个算法都是一样的,测试时为了统一结果,消除因为cpp,python数组从0开始,lua,bash数组从1开始的问题,测试时,cpp,python从-1开始测试。最后测试完成的结果完全一致,来个有意思的截图:
我编码的时候平铺4个putty窗口,正好占满我的19寸屏:)
从左自右依次是cpp,lua,python,bash。

以下是源代码:
CPP:
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <iostream>
using namespace std;
template <typename T>
int binarySearch(const vector<T> &a, const T &x, int & aiTimes)
{
int low = 0;
int high = a.size() - 1;
while(low <= high)
{
++aiTimes;
int mid = (low + high) / 2;
if(a[mid] < x)
{
low = mid + 1;
}
else if(a[mid] > x)
{
high = mid - 1;
}
else
{
return mid;
}
}
return -1;
}
int main(int argc, char * argv[])
{
vector<int > lveci;
lveci.resize(100);
for(int i = 0; i < 100; ++i)
{
lveci[i] = i;
}
int liTimes = 0;
cout << binarySearch(lveci, -1, liTimes);
cout <<"/tcost:" <<liTimes <<endl;
liTimes = 0;
cout << binarySearch(lveci, 0, liTimes);
cout <<"/tcost:" <<liTimes <<endl;
liTimes = 0;
cout << binarySearch(lveci, 1, liTimes);
cout <<"/tcost:" <<liTimes <<endl;
liTimes = 0;
cout << binarySearch(lveci, 2, liTimes);
cout <<"/tcost:" <<liTimes <<endl;
liTimes = 0;
cout << binarySearch(lveci, 100, liTimes);
cout <<"/tcost:"<<liTimes <<endl;
exit(0);
}
LUA:
#!/usr/bin/env lua
function binarySearch(a, v)
low = 1
high = #a
times = 0
while(low <= high) do
times = times + 1
mid = math.floor(( low + high) / 2)
if a[mid] < v then
low = mid + 1
elseif a[mid] > v then
high = mid - 1
else
return mid,times
end
end
return -1,times
end
-- test code
array = {}
for i=1,100 do
array[i] = i
end
print(binarySearch(array, 0))
print(binarySearch(array, 1))
print(binarySearch(array, 2))
print(binarySearch(array, 3))
print(binarySearch(array, 101))
PYTHON:
#!/usr/bin/env python
def binarySearch(a, v):
low = 0
high = len(a) - 1
times = 0
while low <= high:
times += 1
mid = (low + high) // 2
if v > a[mid]:
low = mid + 1
elif v < a[mid]:
high = mid - 1
else :
return mid,times
return -1,times
array = range(100)
print binarySearch(array, -1)
print binarySearch(array, 0)
print binarySearch(array, 1)
print binarySearch(array, 2)
print binarySearch(array, 100)
BASH:
#!/usr/bin/env bash
binarySearch()
{
v=$2
an="$1[@]"
a=${!an}
for i in $a
do
ar[ i]= $i
done
low=1
high=${#ar[*]}
(( times= 0 ))
while (( low <= high ))
do
(( ++times ))
(( mid=( low+high ) /2 ))
#echo -n " mid="$mid
#echo -n " ar[mid]="${ar[mid]}
if (( v > ar[ mid ] ))
then
(( low = mid \+ 1 ))
elif (( v < ar[ mid ] ))
then
(( high = mid \- 1 ))
else
#echo -e "/nTimes="$times
return $mid
fi
done
#echo -e "/nTimes="$times
return -1
}
for ((i= 1; i<= 100; ++i))
do
(( array[ i] = i ))
done
binarySearch array 0
echo -e "$?/t$times"
binarySearch array 1
echo -e "$?/t$times"
binarySearch array 2
echo -e "$?/t$times"
binarySearch array 3
echo -e "$?/t$times"
binarySearch array 101
echo -e "$?/t$times"
exit 0
write by** 九天雁翎** (JTianLing) – www.jtianling.com
阅读全文....
一个无聊男人的疯狂《数据结构与算法分析-C++描述》学习笔记 用C++/lua/python/bash的四重实现(3)
最大子序列和问题
write by** 九天雁翎(JTianLing) –
blog.csdn.net/vagrxie**
«Data Structures and Algorithm Analysis in C++»
--《数据结构与算法分析c++描述》 Mark Allen Weiss著 人民邮电大学出版 中文版第39-43面,图2-5,2-6,2-7,2-8最大子序列和问题的解
总算有点技术含量了,呵呵,虽然说重复实现一下代码不难(不就是相当于翻译嘛),但是算法4为什么是正确的需要好好理解一下。
以下为实现部分:
CPP:
// Maximum contiguous subsequence sum algorithm 1 - 4, worst to best
// From <<Data Structures and Algorithm Analysis in C++>> by Mark Allen Weiss
#include <stdio.h>
#include <stdlib.h>
#include <vector>
**using** **namespace** std;
**int** maxSubSum1(
**const** vector<**int** > &a)
{
**int** maxSum = 0;
**for**(**int** i=0; i<(**int**)a.size(); ++i)
**for**(**int** j=i; j<(**int**)a.size(); ++j)
{
**int** thisSum = 0;
**for**(**int** k=i; k<=j; ++k)
thisSum += a[k];
**if**(thisSum > maxSum)
maxSum = thisSum;
}
**return** maxSum;
}
**int** maxSubSum2(
**const** vector<**int** > &a)
{
**int** maxSum = 0;
**for**(**int** i=0; i<(**int**)a.size(); ++i)
{
**int** thisSum = 0;
**for**(**int** j=i; j<(**int**)a.size(); ++j)
{
thisSum += a[j];
**if**( thisSum > maxSum)
maxSum = thisSum;
}
}
**return** maxSum;
}
**int** max3(**int** a, **int** b, **int** c)
{
**return** ((a < b)?((b < c)?c:b):((a < c)?c:a));
}
**int** maxSumRec(
**const** vector<**int** > &a, **int** left, **int** right)
{
**if**(left == right)
**if**( a[left] > 0)
**return** a[left];
**else**
**return** 0;
**int** center = (left + right) / 2;
**int** maxLeftSum = maxSumRec(a, left, center);
**int** maxRightSum = maxSumRec(a, center + 1, right);
**int** maxLeftBorderSum = 0;
**int** leftBorderSum = 0;
**for**(**int** i=center; i >=left; --i)
{
leftBorderSum += a[i];
**if**(leftBorderSum > maxLeftBorderSum)
maxLeftBorderSum = leftBorderSum;
}
**int** maxRightBorderSum = 0,rightBorderSum = 0;
**for**(**int** j = center + 1; j <= right; ++j)
{
rightBorderSum += a[j];
**if**(rightBorderSum > maxRightBorderSum)
maxRightBorderSum = rightBorderSum;
}
**return** max3(maxLeftSum, maxRightSum, maxLeftBorderSum + maxRightBorderSum);
}
**int** maxSubSum3(**const** vector<**int** > &a)
{
**return** maxSumRec(a, 0, a.size() - 1);
}
**int** maxSubSum4(
**const** vector<**int** > &a)
{
**int** maxSum = 0, thisSum = 0;
**for**( **int** j = 0; j< (**int**)a.size(); ++j)
{
thisSum += a[j];
**if**(thisSum > maxSum)
maxSum = thisSum;
**else** **if**(thisSum < 0)
thisSum = 0;
}
**return** maxSum;
}
**int** main(**int** argc, **char** * argv[])
{
// for easy
**int** a[] = { -2, 11, -4, 13, -5, -2};
vector<**int** > lvec(a, a + **sizeof**(a)/**sizeof**(**int**));
printf("maxSubSum1(lvec)):%d/n",maxSubSum1(lvec));
printf("maxSubSum2(lvec)):%d/n",maxSubSum2(lvec));
printf("maxSubSum3(lvec)):%d/n",maxSubSum3(lvec));
printf("maxSubSum4(lvec)):%d/n",maxSubSum4(lvec));
exit(0);
}
LUA:
#!/usr/bin/env lua
function maxSubSum1(a)
assert(type(a) == "table", "Argument a must be a number array.")
**local** maxSum = 0
**for** i=1,#a do
**for** j=i,#a do
**local** thisSum = 0
**for** k=i,j do
thisSum = thisSum + a[k]
end
**if** thisSum > maxSum **then**
maxSum = thisSum
end
end
end
**return** maxSum
end
function maxSubSum2(a)
assert(type(a) == "table", "Argument a must be a number array.")
**local** maxSum = 0
**for** i=1,#a do
**local** thisSum = 0
**for** j=i,#a do
thisSum = thisSum + a[j]
**if**(thisSum > maxSum) **then**
maxSum = thisSum
end
end
end
**return** maxSum
end
function max3(n1, n2, n3)
**return** (n1 > n2 **and** ((n1 > n3) **and** n1 **or** n3) **or** ((n2 > n3) **and** n2 **or** n3))
end
-- require math
function maxSumRec(a, left, right)
assert(type(a) == "table", "Argument a must be a number array.")
assert(type(left) == "number" **and** type(right) == "number",
"Argument left&right must be number arrays.")
**if** left == right **then**
**if** a[left] > 0 **then**
**return** a[left]
**else**
**return** 0
**end**
**end**
**local** center = math.floor((left + right) / 2)
**local** maxLeftSum = maxSumRec(a, left, center)
**local** maxRightSum = maxSumRec(a, center+1, right)
**local** maxLeftBorderSum = 0
**local** leftBorderSum = 0
**for** i=center,left,-1 do
leftBorderSum = leftBorderSum + a[i]
**if** leftBorderSum > maxLeftBorderSum **then**
maxLeftBorderSum = leftBorderSum
**end**
i = i - 1
**end**
**local** maxRightBorderSum = 0
**local** rightBorderSum = 0
**for** j=center+1,right do
rightBorderSum = rightBorderSum + a[j]
**if** rightBorderSum > maxRightBorderSum **then**
maxRightBorderSum = rightBorderSum
**end**
**end**
**return** max3(maxLeftSum, maxRightSum, maxLeftBorderSum + maxRightBorderSum)
end
function maxSubSum3(a)
assert(type(a) == "table", "Argument a must be a number array.")
**return** maxSumRec(a, 1, 6)
end
function maxSubSum4(a)
assert(type(a) == "table", "Argument a must be a number array.")
**local** maxSum = 0
**local** thisSum = 0
**for** i=1,#a do
thisSum = thisSum + a[i]
**if** thisSum > maxSum **then**
maxSum = thisSum
**elseif** thisSum < 0 **then**
thisSum = 0
**end**
**end**
**return** maxSum
end
-- Test Code
t = { -2, 11, -4, 13, -5, -2 }
print("maxSubSum1(t):" .. maxSubSum1(t))
print("maxSubSum2(t):" .. maxSubSum2(t))
print("maxSubSum3(t):" .. maxSubSum3(t))
print("maxSubSum4(t):" .. maxSubSum4(t))
PYTHON:
#!/usr/bin/env python
# How Short and Clean it is I shocked as a CPP Programmer
**def** maxSubSum1(a):
maxSum = 0
**for** i **in** a:
**for** j **in** a[i:]:
thisSum = 0
**for** k **in** a[i:j]:
thisSum += k
**if** thisSum > maxSum:
maxSum = thisSum
**return** maxSum
**def** maxSubSum2(a):
maxSum = 0
**for** i **in** a:
thisSum = 0
**for** j **in** a[i:]:
thisSum += j
**if** thisSum > maxSum:
maxSum = thisSum
**return** maxSum
**def** max3(n1, n2, n3):
**return** ((n1 **if** n1 > n3 **else** n3) **if** n1 > n2 **else** (n2 **if** n2 > n3 **else** n3))
**def** maxSumRec(a, left, right):
**if** left == right:
**if** a[left] > 0:
**return** a[left]
**else** :
**return** 0
center = (left + right)//2
maxLeftSum = maxSumRec(a, left, center)
maxRightSum = maxSumRec(a, center+1, right)
maxLeftBorderSum = 0
leftBorderSum = 0
**for** i **in** a[center:left:-1]:
leftBorderSum += i
**if** leftBorderSum > maxLeftBorderSum:
maxLeftBorderSum = leftBorderSum
maxRightBorderSum = 0
rightBorderSum = 0
**for** i **in** a[center+1:right]:
rightBorderSum += i
**if** rightBorderSum > maxRightBorderSum:
maxRightBorderSum = rightBorderSum
**return** max3(maxLeftSum, maxRightBorderSum, maxLeftBorderSum + maxRightBorderSum)
**def** maxSubSum3(a):
**return** maxSumRec(a, 0, len(a)-1 )
**def** maxSubSum4(a):
maxSum = 0
thisSum = 0
**for** i **in** a:
thisSum += i
**if** thisSum > maxSum:
maxSum = thisSum
**elif** thisSum < 0:
thisSum = 0
**return** maxSum
**def** test():
t = (-2, 11, -4, 13, -5, -2)
**print** "maxSubSum1:%d" % maxSubSum1(t)
**print** "maxSubSum2:%d" % maxSubSum2(t)
**print** "maxSubSum3:%d" % maxSubSum3(t)
**print** "maxSubSum4:%d" % maxSubSum4(t)
**if** __name__ == '__main__':
test()
BASH:
#!/usr/bin/env bash
绕了我吧。。。。。特别是算法二。。复杂的递归用bash来写就是自虐。Bash似乎本来就不是用来描述算法用的,呵呵,以后没有什么事一般就不把bash搬出来了。
write by** 九天雁翎** (JTianLing) – www.jtianling.com
阅读全文....
这是突然看到才想起来发的,发到我常用的地址中:
http://groups.google.com/group/jiutianfile
那时候带着笔记本,台式机上的源码都发不了,直到现在才想起来:)
很多朋友发邮件或者在博客中问过我为什么没有继续对多鼠标这个东西研究下去,呵呵,在此一并答复了,本人实在是工作忙啊。。。。。。。再次说明一下什么叫 忙,每天工作12个小时以上,每周工作6天,再加上你看看我最近学的东西,lua,python,bash….实在没有时间了,呵呵
那个地方现在有两个五子棋的源码了,一个是单鼠标用左右键来玩的,一个是双鼠标都用左键来玩的。你可以对比一下,看看实现多鼠标需要添加些什么,呵呵,其实也不多,这个示例程序虽然简单,但是却实现了想要的基本功能。
图我也贴一个,不偷懒了。。。。呵呵,毕竟这也算是我以前研究比较久的成果。
展示一下:


class CMyApp : public CWinApp
{
public:
virtual BOOL
InitInstance();
};
class CMainWindow : public CMultiMouseWindow
{
protected:
enum gridState
{ Unputed, PutedWhite,
PutedBlack}; //enum格子的个状态
enum Turn
{blackTurn,whiteTurn}; //enum轮到谁下的状态
enum winnerLast
{NoOne,WhiteWin,BlackWin}; //enum有没有胜利者的状态
Turn m_nextTurn; //下轮执棋者
const static
int nClientSize
= 700; //客户区大小,可改变
const static
int nGridNum
= 20; //格数,可改变
int m_countStep; //目前所下步数
void DrawBoard(CDC &dc); //画棋盘
CRect m_rcGrid[nGridNum][nGridNum]; //棋盘的每个格子的矩形范围
gridState m_stateGrid[nGridNum][nGridNum]; //棋盘每个格子的状态
void DrawWhite(CDC &dc,int i,int j); //下White
void DrawBlack(CDC &dc,int i,int j); //下Black
void ResetGame(); //重新开始游戏
void CheckForGameOver(Turn thisTurn,int i,int j); //查找需不需要结束游戏
BOOL IsWinner(Turn thisTurn,int i,int j); //是否有胜利者
BOOL IsDraw(); //是否平局
public:
CMainWindow();
protected:
afx_msg void OnPaint();
afx_msg void OnLButtonDown(UINT nFlags,CPoint point);
afx_msg void OnNcLButtonDblClk(UINT nHitTest,CPoint point);
DECLARE_MESSAGE_MAP()
};
#include <afxwin.h>
#include <cmath>
#include <memory>
#include "MultiMouseWindow.h"
#include "resource.h"
#include "gobang.h"
CMyApp myApp;
//
//CMyApp的成员函数
//
BOOL CMyApp::InitInstance()
{
m_pMainWnd = new CMainWindow;
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
//
//CMainWindow的消息映射和成员函数定义
//
BEGIN_MESSAGE_MAP(CMainWindow,CFrameWnd)
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
ON_WM_NCLBUTTONDBLCLK()
END_MESSAGE_MAP()
CMainWindow::CMainWindow()
{
//执黑的先下棋,并定下窗口固定宽度
CString wndClassStr = ::AfxRegisterWndClass(CS_DBLCLKS);
Create(wndClassStr,_T("两鼠标五子棋by九天雁翎 (双击标题栏重新开始游戏)"),
WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION
| WS_MINIMIZEBOX);
CRect rect(0,0,nClientSize+2,nClientSize+2);
CalcWindowRect(&rect);
SetWindowPos(NULL,0,0,rect.Width(),rect.Height(),
SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW);
m_hiconCursor[1] = LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_CURSOR));
m_hiconCursor[2] = LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_CURSOR2));
//初始化自定义的成员变量
m_nextTurn = whiteTurn;
m_countStep = 0;
if(m_nMouseNumber
!= 3)
{
MessageBox(_T("请插入个鼠标"));
PostQuitMessage (0);
}
::ZeroMemory(m_rcGrid,nGridNum
* nGridNum * sizeof(CRect));
::ZeroMemory(m_stateGrid,nGridNum
* nGridNum * sizeof(gridState));
}
void CMainWindow::OnPaint()
{
CPaintDC dc(this);
DrawBoard(dc);
DrawMultiCursor();
}
void CMainWindow::DrawBoard(CDC
&dc)
{
CRect rect;
GetClientRect(&rect);
//画背景
CBrush brush(RGB(198,130,66));
dc.FillRect(rect,&brush);
//开始画纵横线,利用rect大小来画线,一是代码重用度高,二是以前做好了
//效率稍微低点,不过将来要是改变客户区大小或用可缩放窗口就可以不改了
//定义画线的画笔并选中
CPen pen(PS_SOLID,2,RGB(0,0,0));
CPen *pOldPen
= dc.SelectObject(&pen);
//求方格宽高
int nGridWidth
= nClientSize / nGridNum ;
int nGridHeight
= nClientSize / nGridNum
;
//计算每个方格矩形范围
for(int
i = 0; i
< nGridNum; ++i)
for(int
j = 0; j
< nGridNum; ++j)
{
m_rcGrid[i][j] = CRect(rect.left \+ (nGridWidth
* j),
rect.top \+ (nGridHeight * i),
rect.left \+ nGridWidth +(nGridWidth
* j),
rect.top \+ nGridHeight \+ (nGridHeight
* i));
}
for(int
i = 0; i
<= nGridNum; ++i) //画横线
{
int y
= (nGridHeight * i)
\+ rect.top;
dc.MoveTo(rect.left,y);
dc.LineTo(rect.right,y);
}
for(int
i = 0; i
<= nGridNum; ++i) //画竖线
{
int x
= (nGridWidth * i)
\+ rect.left;
dc.MoveTo(x,rect.top);
dc.LineTo(x,rect.bottom);
}
//画下已经下好的棋
for(int
i=0; i<nGridNum; ++i)
for(int
j=0; j<nGridNum; ++j)
{
if(m_stateGrid[i][j] == Unputed)
{
continue;
}
else if(m_stateGrid[i][j] == PutedWhite)
{
DrawWhite(dc,i,j);
}
else if(m_stateGrid[i][j] == PutedBlack)
{
DrawBlack(dc,i,j);
}
}
}
//左键下黑
void CMainWindow::OnLButtonDown(UINT
nFlags, CPoint
point)
{
if(m_pSiRawMouse[1].usButtonFlags & RI_MOUSE_LEFT_BUTTON_DOWN)
{
//若本轮不属黑,即不响应
if(m_nextTurn
!= blackTurn)
return;
for(int
i = 0; i<nGridNum; ++i)
for(int j = 0; j<nGridNum;
++j)
{
CPoint pt(m_pSiRawMouse[1].X,m_pSiRawMouse[1].Y);
ScreenToClient(&pt);
if(m_rcGrid[i][j].PtInRect(pt) && m_stateGrid[i][j] == Unputed)
{
CClientDC dc(this);
m_nextTurn = whiteTurn;
m_stateGrid[i][j] = PutedBlack;
DrawBlack(dc,i,j);
CheckForGameOver(blackTurn,i,j);
}
}
m_pSiRawMouse[1].usButtonFlags &= ~RI_MOUSE_LEFT_BUTTON_DOWN;
}
if(m_pSiRawMouse[2].usButtonFlags & RI_MOUSE_LEFT_BUTTON_DOWN)
{
//若本轮不属白,即不响应
if(m_nextTurn
!= whiteTurn)
return;
for(int
i = 0; i<nGridNum; ++i)
for(int j = 0; j<nGridNum;
++j)
{
CPoint pt(m_pSiRawMouse[2].X,m_pSiRawMouse[2].Y);
ScreenToClient(&pt);
if(m_rcGrid[i][j].PtInRect(pt) && m_stateGrid[i][j] == Unputed)
{
CClientDC dc(this);
m_nextTurn = blackTurn;
m_stateGrid[i][j] = PutedWhite;
DrawWhite(dc,i,j);
++m_countStep;
CheckForGameOver(whiteTurn,i,j);
}
}
m_pSiRawMouse[2].usButtonFlags &= ~RI_MOUSE_LEFT_BUTTON_DOWN;
}
}
void CMainWindow::DrawBlack(CDC
&dc, int
i, int j)
{
CRect rect(m_rcGrid[i][j]);
rect.DeflateRect(1,1);
dc.SelectStockObject(BLACK_BRUSH);
dc.Ellipse(rect);
}
void CMainWindow::DrawWhite(CDC
&dc, int
i, int j)
{
CRect rect(m_rcGrid[i][j]);
rect.DeflateRect(1,1);
dc.SelectStockObject(WHITE_BRUSH);
dc.Ellipse(rect);
}
void CMainWindow::CheckForGameOver(Turn
thisTurn,int
i,int j)
{
if(IsWinner(thisTurn,i,j))
{
if(thisTurn
== blackTurn)
{
CString string;
string.Format(_T("GOOD! Black Wins in %d steps."),m_countStep);
MessageBox(string,_T("Game Over!"));
ResetGame();
}
else if(thisTurn == whiteTurn)
{
CString string;
string.Format(_T("GOOD! White Wins in %d steps."),m_countStep);
MessageBox(string,_T("Game Over!"));
ResetGame();
}
}
else if(IsDraw())
{
MessageBox(_T("OK,It's
draw."),_T("Draw Game!"));
ResetGame();
}
}
//此为本软件最主要的部分,即检测是否有五个棋连在一起
//以横纵和两条对角线的方向分别检测,感觉比较笨
//暂时不知道有没有更好的办法,望来信赐教
BOOL CMainWindow::IsWinner(Turn thisTurn,int i,int j)
{
int count
= 1;
gridState checkFor; //状态对比值
if(thisTurn
== blackTurn)
checkFor = PutedBlack;
else if(thisTurn == whiteTurn)
checkFor = PutedWhite;
//横方向检测
for(int
m=1; m<5;
++m)
{
if(j
\- m > 0 && m_stateGrid[i][j-m] == checkFor)
++count;
else
break;
}
for(int
m=1; m<5;
++m)
{
if(j
\+ m < nGridNum
&& m_stateGrid[i][j+m] == checkFor)
++count;
else
break;
}
if(count
>=5)
return TRUE;
count = 1;
//竖方向检测
for(int
m=1; m<5;
++m)
{
if(i-m>0 && m_stateGrid[i-m][j] == checkFor)
++count;
else
break;
}
for(int
m=1; m<5;
++m)
{
if(i+m<nGridNum
&& m_stateGrid[i+m][j] == checkFor)
++count;
else
break;
}
if(count
>=5)
return TRUE;
count = 1;
//左上至右下方向检测
for(int
m=1; m<5;
++m)
{
if(i-m>0 && j-m>0 && m_stateGrid[i-m][j-m] == checkFor)
++count;
else
break;
}
for(int
m=1; m<5;
++m)
{
if(i+m<nGridNum
&& j+m<nGridNum && m_stateGrid[i+m][j+j] == checkFor)
++count;
else
break;
}
if(count
>=5)
return TRUE;
count = 1;
//右上至左下方向检测
for(int
m=1; m<5;
++m)
{
if(i-m>0 && j+m<nGridNum
&& m_stateGrid[i-m][j+m] == checkFor)
++count;
else
break;
}
for(int
m=1; m<5;
++m)
{
if(i+m<nGridNum
&& j-m>0
&& m_stateGrid[i+m][j-m] == checkFor)
++count;
else
break;
}
if(count
>=5)
return TRUE;
return FALSE;
}
//BOOL
CMainWindow::OnSetCursor(CWnd *pWnd, UINT nHitTest, UINT message)
//{
// if(nHitTest == HTCLIENT)
// {
// if(m_nextTurn == blackTurn)
// {
// ::SetCursor(::AfxGetApp()->LoadCursor(IDC_black));
// return TRUE;
// }
// else if(m_nextTurn == whiteTurn)
// {
// ::SetCursor(::AfxGetApp()->LoadCursor(IDC_white));
// return TRUE;
// }
// }
// return
CFrameWnd::OnSetCursor(pWnd,nHitTest,message);
//}
//当都下满了,即为平局
BOOL CMainWindow::IsDraw()
{
int i,j;
for(i=0;
i<nGridNum;
++i)
for(j=0;
j<nGridNum;
++j)
{
if(m_stateGrid[i][j]==Unputed)
break;
}
if(i==nGridNum && j==nGridNum)
return TRUE;
else
return FALSE;
}
void CMainWindow::ResetGame()
{
m_nextTurn = whiteTurn;
m_countStep = 0;
::ZeroMemory(m_stateGrid,nGridNum
* nGridNum * sizeof(gridState));
Invalidate();
}
void CMainWindow::OnNcLButtonDblClk(UINT
nHitTest, CPoint
point)
{
if(nHitTest
== HTCAPTION)
{
ResetGame();
}
return CFrameWnd::OnNcLButtonDblClk(nHitTest,point);
}
#ifndef ___MULTI_MOUSE_WINDOW_H__
#define ___MULTI_MOUSE_WINDOW_H__
class CMultiMouseWindow :
public CFrameWnd
{
typedef struct SiRawMouse
{
long X;
long Y;
USHORT usButtonFlags;
HANDLE hDevice;
}SiRawMouse,*pSiRawMouse; //输入设备鼠标的自定义类型数组
//判定是否为系统鼠标的函数
private:
void InitRawInput();
bool IsRootMouse(TCHAR cDeviceString[]);
void CheckBound(long &x,long &y);
protected:
pSiRawMouse m_pSiRawMouse;
TCHAR m_mousePostion[256]; // 鼠标位置缓存
HICON m_hiconCursor[10];
//绘制的鼠标
int m_nMouseNumber;
//鼠标数量
void DrawMultiCursor();
public:
CMultiMouseWindow();
protected:
DECLARE_MESSAGE_MAP()
public:
protected:
virtual LRESULT
WindowProc(UINT
message, WPARAM
wParam, LPARAM
lParam);
public:
afx_msg void OnDestroy();
};
#endif // ___MULTI_MOUSE_WINDOW_H__
#include <afxwin.h>
#include <cmath>
#include "MultiMouseWindow.h"
#include "resource.h"
#include <Windows.h>
BEGIN_MESSAGE_MAP(CMultiMouseWindow,CFrameWnd)
ON_WM_DESTROY()
END_MESSAGE_MAP()
CMultiMouseWindow::CMultiMouseWindow():m_nMouseNumber(0)
{
Create(NULL,_T("Hello"),WS_OVERLAPPEDWINDOW);
for(int
i=0; i
< 10; ++i)
{
m_hiconCursor[i] = LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_CURSOR));
}
ShowCursor(false);
InitRawInput();
}
void CMultiMouseWindow::InitRawInput()
{
UINT nDevices; //输入设备的数量
//第一次调用GetRawInputDeviceList获得输入设备的数量
GetRawInputDeviceList(NULL, &nDevices,
sizeof(RAWINPUTDEVICELIST));
//第二次调用GetRawInputDeviceList获得RawInputDeviceList数组
PRAWINPUTDEVICELIST pRawInputDeviceList;// =
new RAWINPUTDEVICELIST[nDevices];
pRawInputDeviceList = (RAWINPUTDEVICELIST *)calloc(nDevices,sizeof(RAWINPUTDEVICELIST));
GetRawInputDeviceList(pRawInputDeviceList, &nDevices,
sizeof(RAWINPUTDEVICELIST));
//获得输入设备中鼠标的数量
for(int
i=0; i
< static_cast<int>(nDevices); ++i)
{
if(pRawInputDeviceList[i].dwType == RIM_TYPEMOUSE)
{
++m_nMouseNumber;
}
}
//获得输入设备鼠标的自定义类型数组,其中索引为的为系统鼠标
m_pSiRawMouse = (SiRawMouse *)calloc(m_nMouseNumber,sizeof(SiRawMouse));
LPVOID lpb;
UINT dwSize;
for(int
i=0,j=1; i < static_cast<int>(nDevices)
&& j < m_nMouseNumber;
++i)
{
if(pRawInputDeviceList[i].dwType == RIM_TYPEMOUSE)
{
//连续两次调用GetRawInputDeviceInfo以读取RIDI_DEVICENAME
//并通过此值判断是否为系统鼠标
GetRawInputDeviceInfo(pRawInputDeviceList[i].hDevice,RIDI_DEVICENAME,NULL,&dwSize);
lpb = malloc(sizeof(LPVOID) * dwSize);
GetRawInputDeviceInfo(pRawInputDeviceList[i].hDevice,RIDI_DEVICENAME,lpb,&dwSize);
TCHAR *deviceName = (TCHAR*)lpb;
//将系统鼠标的保存在索引中,其他依次列在后面
if(IsRootMouse(deviceName))
{
m_pSiRawMouse[0].hDevice = pRawInputDeviceList[i].hDevice;
m_pSiRawMouse[0].X = 0;
m_pSiRawMouse[0].Y = 0;
m_pSiRawMouse[0].usButtonFlags = 0;
}
else
{
m_pSiRawMouse[j].hDevice = pRawInputDeviceList[i].hDevice;
m_pSiRawMouse[j].X = 0;
m_pSiRawMouse[j].Y = 0;
m_pSiRawMouse[j].usButtonFlags
= 0;
++j;
}
free(lpb);
}
}
free(pRawInputDeviceList);
pRawInputDeviceList = NULL;
//初始化个RawInput设备
RAWINPUTDEVICE Rid[1]; //分配RawInput设备的空间
Rid[0].usUsagePage = 0x01;
Rid[0].usUsage = 0x02;
Rid[0].dwFlags = 0;
Rid[0].hwndTarget = NULL;
//注册RawInput设备
if (RegisterRawInputDevices(Rid, 1, sizeof (Rid [0])) == FALSE)
{
MessageBox(_T("RawInput
Register Error."));
}
return ;
}
bool CMultiMouseWindow::IsRootMouse(TCHAR
cDeviceString[])
{
//一般系统鼠标DeviceName的前字节
TCHAR cRootString[]
= _T("//??//Root#RDP_MOU#0000#");
//通过比较前个字节判断是否为系统鼠标
if (wcslen(cDeviceString) < 22)
{
return false;
}
for (int
i = 0; i
< 22; i++)
{
if (cRootString[i] != cDeviceString[i])
{
return false;
}
}
return true;
}
void CMultiMouseWindow::CheckBound(long
&x,long
&y)
{
CClientDC dc(this);
if(x<0)
x = 0;
if(y<0)
y = 0;
//最大值由GetDeviceCaps函数获取以适应不同系统设置
if(x>(long)dc.GetDeviceCaps(HORZRES))
x = (long)dc.GetDeviceCaps(HORZRES);
if(y>(long)dc.GetDeviceCaps(VERTRES))
y = (long)dc.GetDeviceCaps(VERTRES);
}
//CMainWindow
mesage map and member functions
void CMultiMouseWindow::DrawMultiCursor()
{
CClientDC dc(this);
for(int
i=1; i<m_nMouseNumber; ++i)
{
POINT pt = { m_pSiRawMouse[i].X, m_pSiRawMouse[i].Y };
ScreenToClient(&pt);
dc.DrawIcon(pt, m_hiconCursor[i]);
}
}
LRESULT CMultiMouseWindow::WindowProc(UINT
message, WPARAM
wParam, LPARAM
lParam)
{
// TODO: 在此添加专用代码和/或调用基类
LPVOID lpb;
UINT dwSize;
RAWINPUT *raw;
switch (message)
{
//响应WM_INPUT消息为本程序的主要部分
case WM_INPUT:
{
::GetRawInputData((HRAWINPUT)lParam,
RID_INPUT, NULL,
&dwSize,
sizeof(RAWINPUTHEADER));
lpb = malloc(sizeof(LPVOID) * dwSize);
if (lpb == NULL)
{
return 0;
}
::GetRawInputData((HRAWINPUT)lParam,
RID_INPUT, lpb,
&dwSize,
sizeof(RAWINPUTHEADER));
raw = (RAWINPUT*)lpb;
if (raw->header.dwType == RIM_TYPEMOUSE)
//判断是否为鼠标信息
{
raw->data.mouse.usFlags = MOUSE_MOVE_ABSOLUTE;
for(int i=1; i<m_nMouseNumber;
++i)
{
if(m_pSiRawMouse[i].hDevice == raw->header.hDevice)
{
POINT pt = {m_pSiRawMouse[i].X,m_pSiRawMouse[i].Y};
ScreenToClient(&pt);
CRect rect;
rect.left = pt.x \- 30;
rect.right = pt.x \+ 30;
rect.top = pt.y \- 30;
rect.bottom = pt.y \+ 30;
m_pSiRawMouse[i].X += raw->data.mouse.lLastX;
m_pSiRawMouse[i].Y += raw->data.mouse.lLastY;
InvalidateRect(&rect,TRUE);
CheckBound(m_pSiRawMouse[i].X,m_pSiRawMouse[i].Y);
m_pSiRawMouse[0].X = m_pSiRawMouse[i].X;
m_pSiRawMouse[0].Y = m_pSiRawMouse[i].Y;
if(raw->data.mouse.usButtonFlags
!= 0)
{
m_pSiRawMouse[i].usButtonFlags
= raw->data.mouse.usButtonFlags;
}
}
::SetCursorPos(m_pSiRawMouse[0].X,
m_pSiRawMouse[0].Y);
}
}
free(lpb);
return 0;
}
}
return CFrameWnd::WindowProc(message,
wParam, lParam);
}
void CMultiMouseWindow::OnDestroy()
{
CFrameWnd::OnDestroy();
// TODO: 在此处添加消息处理程序代码
if(m_pSiRawMouse
!= NULL)
{
free(m_pSiRawMouse);
}
for(int
i=0; i
< 10; ++i)
{
DestroyIcon(m_hiconCursor[i]);
}
PostQuitMessage (0);
}
阅读全文....