cexer

cexer
posts - 12, comments - 334, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理
共4页: 1 2 3 4 
re: 将 Timer 对象化[未登录] cexer 2013-06-25 12:38
class CTest
{
public:
CTest()
: m_value( 0x12345678 )
{
build_proc();
}

public:
LRESULT CALLBACK member_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
{
printf( "hwnd:%d, msg:%d, wparam:%d, lparam:%d\n", (int)hwnd, (int)msg, (int)wparam, (int)lparam );
printf( "CTest::value: 0x%x\n", m_value );
return 0;
}

void build_proc()
{
unsigned char* proc = m_proc;
///////////////////////// Prolog.
__CODE( 1, ( 0x55 ) ); // 55 push ebp
__CODE( 2, ( 0x8B, 0xEC ) ); // 8B EC mov ebp,esp
__CODE( 6, ( 0x81, 0xEC, 0xC0, 0x00, 0x00, 0x00 ) ); // 81 EC C0 00 00 00 sub esp,0C0h
__CODE( 1, ( 0x53 ) ); // 53 push ebx
__CODE( 1, ( 0x56 ) ); // 56 push esi
__CODE( 1, ( 0x57 ) ); // 57 push edi
__CODE( 6, ( 0x8D, 0xBD, 0x40, 0xFF, 0xFF, 0xFF ) ); // 8D BD 40 FF FF FF lea edi,[ebp+FFFFFF40h]
__CODE( 5, ( 0xB9, 0x30, 0x00, 0x00, 0x00 ) ); // B9 30 00 00 00 mov ecx,30h
__CODE( 5, ( 0xB8, 0xCC, 0xCC, 0xCC, 0xCC ) ); // B8 CC CC CC CC mov eax,0CCCCCCCCh
__CODE( 2, ( 0xF3, 0xAB ) ); // F3 AB rep stos dword ptr es:[edi]

////////////////////// Codes
__CODE( 3, ( 0x8B, 0x45, 0x14 ) ); // 8B 45 14 mov eax,dword ptr [ebp+14h] [lparam]
__CODE( 1, ( 0x50 ) ); // 50 push eax
__CODE( 3, ( 0x8B, 0x45, 0x10 ) ); // 8B 45 10 mov eax,dword ptr [ebp+10h] [wparam]
__CODE( 1, ( 0x50 ) ); // 50 push eax
__CODE( 3, ( 0x8B, 0x55, 0x0C ) ); // 8B 55 0C mov edx,dword ptr [ebp+0Ch] [msg]
__CODE( 1, ( 0x52 ) ); // 52 push edx
__CODE( 3, ( 0x8B, 0x45, 0x08 ) ); // 8B 45 08 mov eax,dword ptr [ebp+8] [hwnd]
__CODE( 1, ( 0x50 ) ); // 50 push eax
__CODE( 1, ( 0xB9 ) ); __PTR( this ); // B9 ?? ?? ?? ?? mov ecx, this
__CODE( 1, ( 0x51 ) ); // 51 push ecx
__CALL( &CTest::member_proc ); // E8 ?? ?? ?? ?? call CTest::member_proc

/////////////////////// Epilog.
__CODE( 1, ( 0x5F ) ); // 5F pop edi
__CODE( 1, ( 0x5E ) ); // 5E pop esi
__CODE( 1, ( 0x5B ) ); // 5B pop ebx
__CODE( 6, ( 0x81, 0xC4, 0xC0, 0x00, 0x00, 0x00 ) ); // 81 C4 C0 00 00 00 add esp,0C0h
__CODE( 2, ( 0x8B, 0xE5 ) ); // 8B E5 mov esp,ebp
__CODE( 1, ( 0x5D ) ); // 5D pop ebp
__CODE( 3, ( 0xC2, 0x10, 0x00 ) ); // C2 10 00 ret 10h

DWORD old = 0;
VirtualProtect( &m_proc, sizeof(m_proc), PAGE_EXECUTE_READWRITE, &old );
}

WNDPROC get_proc()
{
return (WNDPROC)(void*)m_proc;
}

public:
char m_proc[1024];
int m_value;
};


int main( int argc, char** argv )
{
CTest test;
WNDPROC proc = test.get_proc();
proc( (HWND)1, 2, 3, 4 );

return 0;
}
re: 将 Timer 对象化[未登录] cexer 2013-06-25 12:37
#include <iostream>
#include <string>
#include <windows.h>
#include <cstdio>
using namespace std;


template<class T, class S>
inline T union_cast( S s )
{
union
{
T t;
S s;
} u;
u.s = s;
return u.t;
}

#define __CODE1( a ) \
*(proc++) = a

#define __CODE2( a1, a2 ) \
*(proc++) = a1; \
*(proc++) = a2

#define __CODE3( a1, a2, a3 ) \
*(proc++) = a1; \
*(proc++) = a2; \
*(proc++) = a3

#define __CODE4( a1, a2, a3, a4 ) \
*(proc++) = a1; \
*(proc++) = a2; \
*(proc++) = a3; \
*(proc++) = a4

#define __CODE5( a1, a2, a3, a4, a5 ) \
*(proc++) = a1; \
*(proc++) = a2; \
*(proc++) = a3; \
*(proc++) = a4; \
*(proc++) = a5

#define __CODE6( a1, a2, a3, a4, a5, a6 ) \
*(proc++) = a1; \
*(proc++) = a2; \
*(proc++) = a3; \
*(proc++) = a4; \
*(proc++) = a5; \
*(proc++) = a6

#define __CODE( n, a ) __CODE##n a

#define __PTR( p ) \
{ \
*( (void**)proc ) = union_cast<void*>(p);\
proc += sizeof(void*); \
}

#define __CALL( p ) \
{ \
*(proc++) = 0xE8; \
unsigned char* pfunc = union_cast<unsigned char*>(p);\
*( (ptrdiff_t*)proc ) = pfunc - proc - 4; \
proc += 4; \
}
re: 将 Timer 对象化[未登录] cexer 2013-06-25 12:35
其实可以彻底摆脱那个static,办法就是在直接(或者使用JITAssembler)手动生成static的代码,跟thunk替换栈上数据一样的道理。只是这样生成的代码同时与编译器和CPU绑定了,与编译器绑定是因为各个编译器生成的调用成员函数的方式可能不一样。
Android上有很多现成的HTTP服务端应用,另外busybox自带httpd。不过楼主尝试一下也不是坏事。
一楼的同志,好!果然是江湖中人,够爽快!
二楼的同事,人家也没说是赚钱的啊,咋就得了个”在错误的道路上越走越远“的结论呢。
我看挺好的!
我觉得boost::pool的ordered_malloc和ordered_free函数是有必要的,它们是作为ordered pool的接口部分而存在的,使用场景不一样,和malloc和free是没有可比性。

boost::pool中有一个函数malloc_n用来申请几块连续的partition。一个block是以partition为粒度划分,以单向链表链接起来,多次malloc/free的动作之后,这些partition在链表中的逻辑顺序和它们在内存中的实际顺序极有可能是不一致的,此时要得到连续的partition,必须要遍历链表,找出在内存中实际连续的partition,并将它们在链表中的顺序也重新以连续的顺序链接,然后返回给调用者。

如果将链表的顺序与内存中实际顺序的不一致程度叫做它的无序度,显然这个遍历操作的耗费的时间,和链表的无序度是成正比的。如果这种malloc_n的操作很频繁,那么这种遍历链表重新链接的操作会很费时。

这种频繁申请多个连续partition的场景下正是使用ordered pool的地方,正如pool作者在代码写的注释那样:
// Note: if you're allocating/deallocating n a lot, you should
// be using an ordered pool.

ordered pool内部的所有partition链表无序度为0,即所有partition在内存中的实际顺序和它们在链表中的逻辑顺序完全一致,对于malloc_n这样的请求,可以更快满足。也因此boost::pool提供了ordered版本的操作接口,这组接口保证每次free之后链表的顺序不会乱。

不过,我个人觉得block以partition为粒度划分是没必要的,可以在收到请求的时候按实际需求划分,尽力使整个块保持连续,可以极大地避免产生碎片,降低ordered_malloc的ordered_free的时间。

当然ordered_xxx函数不应该和其它的函数混用,否则就是陪了夫人又折兵的买卖,即浪费了ordered_xxx的时间,又无法得到更快malloc_n的好处。
当使用CreateProcess传入命令行参数的时候,argv[0]可以是任何值,不一定是程序路径或程序名。
搞笑啊,楼上你冒用ID很光彩?看来被人骂傻逼没骂错
那些出口就骂人的,都是些畜牲吗?你爹妈没教过你怎么说人话?
楼主你的文章质量都不错,就是有点偏离群众口味。
嵌入式?如果是一般应用开发面试出这种题有点不靠谱,写产品的人哪能钻到这样的细节里去。
@三夏健
说得有道理,并且我补充你一下,“纸上得来终觉浅,绝知此事要躬行”,这句话不能太单纯地理解。首先第一步要“纸上得来”,如果自己的理解已经超越了纸面上能给你的,然后就再“躬行”。理论基础要扎实,实践才能有的放矢。另外有些东西是知识结构的基本结点,比如说“1+1=2”,本来就是“浅”的,没必要“躬行”。
re: C++杂谈[未登录] cexer 2011-07-14 20:45
应用的框架到了比较高的阶段,所面临的问题的宽度和深度已经完全超越了语言之间的微妙界线,所以像 shared_ptr 这类语言底层的东西,不能拿到这种场合来说。boost 是个大杂哙,有些东西用起来很不错,比如说 shared_ptr,有些东西不是拿来用的,是实验室产物,像你说的 tuple 确实是不大实用。
上面有人竟然拿 auto_ptr 来说多 CPU 并发。这种问题就好像,你在路上捡到一块鼠标垫,然后就开始发愁,还配点啥才能玩上魔兽世界。
re: c++处女类[未登录] cexer 2011-07-14 20:30
大哥,C++是语言的艺术,讲究说学逗唱,你直接摆代码是怎么个回事啊。
这跟在没在首页可没关系。我是说解决问题的方式,像导出符号这种按步就搬的套路,自己去猜,从学习成长的角度来说,不如系统地找本书看看来得有效率。虽然自己试,长久下来也能积累知识,只是学习到了一个阶段就会有瓶颈,需要把零散知识融汇贯通的时候就不如那么随心所欲了,会有那种怎么也打不通仁督二脉的感觉。另外,我是以为以为楼主写了一个类似 dll2lib 的工具,就点进来的,有点上当的感觉啊。
知识结构得系统,完整,别自己东弄一点西猜一点,零零散散散的,自己不懂就找个教程找本书从头至尾好好看看,要不出了问题都不知道在哪里.
不管是不是牛人,与人讨论的时候,都不要摆出“蹉,来食”的施舍姿态。作为做技术的,术业有专攻,没有谁是十项全能宇宙无敌的,所以不要轻易否定别人的成果。作为做人的基本原则,行前三思言必三省,考虑一下自己的言行是否会破坏别人的心情。

模式匹配我了解的不多,不过词法分析正则表达式有穷自动机什么的,是成熟很多年的技术了,只是能沉下心来啃书写代码的人不多,能有自己思考的人更少,因此我很佩服楼主的编译器功底。《代码之美》看过,与想像中的有差距,比较鸡肋的一本书,楼主所说的代码刚好也看过,饭同学的代码确实不如它精巧,但是贵在原创,我能够体会他的成就感被人击破时的气愤。

既然饭同学说你态度有问题,你就有则改之,无则加勉好了。如果有一个人说你有问题,你可以不必太在意。如果有两个人说你有问题,你就要想想到底是谁的问题了。如果有三个人说你有问题,那就是你一定有问题了。


不错的文章,楼主是个低调做事的人,支持!
我拜托把这难看的界面改改,回复功能增强一点。
实现一个functor,除了众所周知的功能,我建议楼主再考虑以下这两点:

1 给返回void(无返回值)的functor绑定一个有返回值的可调用体,这个貌似boost::function是支持的。这功能是需要的,因为实际上很多时候,我们的functor不关心返回值,就好像某个消息无论你返回什么系统都执行相同的操作,这种情况下,如果绑定的可调用体返回了什么值,functor应该检测到并使用某种屏蔽手段,而不是报错“对不起,返回类似不匹配”。

2 给返回非void(有返回值)的functor绑定一个返回void(无返回值)的可调用体,这个貌似boost::function不支持。这功能是需要的,因为有时候functor需要一个明确的返回值,就像WM_ERASEBKGND消息,根据返回TRUE或FALSE的不同,系统会决定是否自己来画窗口背景。如果可调用体返回void(无返回值),那么functor应该检测到它,并在返回时提供一个默认的返回值,而不是在绑定时就报错“对不起,返回类似不匹配”。

以上两点都可以通过模板元编程实现,通过typetraits检测返回值,并根据functor和可调用体之两者返回值的不同组合,来选择不同的返回策略。

另外还有,如果想绑定系统函数如Windows API,或者其它第三方提供的函数,你还需要考虑调用约定的问题,因为 __stdcall,__cdecl,__fastcall的不同,都会使函数签名不同。有多少种调用约定,functor的绑定函数的数量就需要乘以多少,这是个体力活,可以用预处理元和文件包含来减少体力消耗。
@飞舞的烟灰缸
【function的多播实现还是有点麻烦。
+= 倒是简单,但是要实现 -=这样的操作就需要判断function相等,麻烦就比较大了】

我想你指的麻烦,可能是你想实现这样的功能:
// 加入
on_event += message_handler( handler );
// 删除
on_event -= message_handler( handler );

// 加入
on_event += message_handler ( &Window::handle_resized,this );
// 删除
on_event -= message_handler ( &Window::handle_resized,this );

这样的方式实现其实也不难,但是绝对需要 RTTI 那种东西的支持,另外还有一个问题就是这样的方式使用用起来太麻烦,比如:

// 加入
on_event += message_handler ( &Window::handle_resized,this );
// 删除
on_event -= message_handler ( &Window::handle_resized,this );

如果库提供了类似 boost::asio 那种参数绑定的功能,看看有多麻烦:
// 加入
on_event += message_handler ( &Window::handle_resized,this,Argument::size,Argument::caption );
// 删除
on_event -= message_handler ( &Window::handle_resized,this,Argument::size,Argument::caption );


其实这个功能有比你个简单得多的实现方式,给一个 handler 绑定一个名字,到时删除这个名字即可,见下例:
// 加入
on_event += message_handler ( name,handler );
// 删除
on_event -= message_handler( name );


@OwnWaterloo
【借宝地请教一下~ 对于handler, 是否需要如此的动态?
A. 完全动态
B. 完全静态
C. 两者结合, 动态部分自己实现。
如上, 需要A那种灵活性么? 缺之不可?
因为我自己确实没写过几个gui程序, "作为一个库的用户", 没有足够的这方面的使用经验。】


说说我的看法:A并不是必须的,比如说MFC,可以算是完全静态的,其所有的消息映射代码都是用宏堆死的选择语句,提供的动态功能基本上无,优势很明显,一个类的所有实例共享一份映射数据,具有空间优势。但是一个独立的对象不能有它自己处理事件的方式,这明显不是面向对象的封装方式,运行时灵活性大打折扣,这样说起来很模糊,仍然举例MFC来说明:

比如说使用MFC运行时创建的一个按钮,怎么样才能把这个按钮的消息加到消息映射里去呢?对不起,不能。因为按钮ID是动态生成的,那些映射宏什么的到运行时都成了浮云。解决办法当然也有:回到原始时代--要么在父窗口的消息回调函数里检查WM_COMMAND和BN_CLICK,二是任劳任怨地去自己重写一个按钮类,并在自己的消息回调里响应WM_COMMAND的反射消息并检测BN_CLICK,然后进行处理--看看有多麻烦。

所以我觉得一个好的库,应该能够同时提供两者,就是你说的C。同时支持静态和动态的映射方式,关键是库要实现得好,在没有使用动态映射的时候,这个动态映射的功能就好像不存在一样,这样大多数时候既能够享受空间优势,在需要的时候又能发挥它的动态优势。免得用户要在在空间优势和灵活性之间做出痛苦的选择。
不说MFC那种陈旧的消息机制,用boost::function,boost::bind,boost::signal之类的来实现消息机制,也算有点out了,而且这种实现已经有框架使用(比如SmartWin++),不再具有新意了,其实强大的C++可以完成远比它们更高级的功能,列举三点:

1 框架可以给同一个消息源添加任意多个个完全不同的消息处理器,类似函数重载,可以将这个功能命名为处理器重载。仅仅是类似boost:function和boost:signal那种固定签名的工具,是无法完成这样的功能的,如下所示:

// 自由函数
void global_handler(){}

// 一段脚本
Script script_handler = create_script( /*这是一段脚本*/ );

// 系统函数
int WINAPI MessageBox( HWND,LPCTSTR,LPCTSTR,UINT );

// 具有不同参数的成员函数
viod Class:memeber_handler_void(){}
void Class::member_handler_size( Size size ){}
void Class::member_handler_int( int x,int y ){}

// 消息映射
Window window;
Class object;
window.on_resized += message_handler( global_handler );
window.on_resized += message_handler( script_handler );
window.on_resized += message_handler( &Class::member_handler_void,&object );
window.on_resized += message_handler( &Class::member_handler_size,&object );
window.on_resized += message_handler( &Class::member_handler_int ,&object );
window.on_resized += message_handler( &::MessageBox,NULL,"Hello World",MB_OK );


2 框架能够进行返回值检查(众所周知的,在windows消息系统中,很多消息需要进行特殊的返回值检查,强WM_ERASEBKGND之类的),比如一个closing消息,如果处理器返回非void类型,并且能够强制转换为true,则表明它同意关闭窗口,如果能够强制转换为false,则表示它阻止框架进行关闭,如果处理器返回值类型是void,则表示它不关心关闭与否,让框架进行默认处理。这个功能可以命名为返回值智能检测。如下所示:

// 这个处理器不关心窗口是否要关闭
void handle_closing_dont_care( Window* window )
{
}

// 这个处理器告诉框架不要关闭
LRESULT handle_closing_no()
{
return 0;
}

// 这个处理器告诉框架进行关闭
bool handle_closing_yes()
{
return true;
}

// 消息映射,窗口最终是否关闭取决于最后一个处理器的决定。
Window window
window.on_closing += message_handler( handle_closing_dont_care );
window.on_closing += message_handler( handle_closing_no );
window.on_closing += message_handler( handle_closing_yes );


3 框架提供消息分解者,允许使用者自己组合自己的处理器参数,类似boost::asio的handler绑定bytes_transfered和error_code的方式,可以把这个功能命名为消息参数任意组合,比如同一个消息resized,有的处理器需要当前窗口的大小,有的处理器需要当前窗口的标题,有的处理器同时需要两者,则消息映射时手动组合消息分解者即可。更进一步,对于某些预注册的签名,框架可以智能提供分解者,免去使用者手动提供的麻烦,例如签名 (Window*,Size) 已经预先注册,则具有这个签名的处理器不用手动指定消息分解器
示例代码如下:

// 这个处理器需要当前窗口大小
void handle_resized_size( Size size ){}

// 这个处理器需要窗口标题
void handle_resized_text( String caption ){}

// 这个处理器都需要
void handle_resized_both( Size size,String caption )


// 消息映射的时候,同时指示自己需要什么参数
Window window
window.on_resized += message_handler( handle_resized_size,Window::Argument::size )
window.on_resized += message_handler( handle_resized_text,Window::Argument::text )
window.on_resized += message_handler( handle_resized_both,Window::Argument::size,Window::Argument::text )


void handle_resized( Window* window,Size size ){}
window.on_resized += message_handler( handle_resized );

4 但是。。。。。在C++.0x.lamda的面前,以上一切都成了浮云。把lamda作为消息处理器,它自身就同时提供了以上1,2,3的所有优点,简洁优雅使用方便,物美价廉童叟无欺,以上例说明:

// 这个处理器需要当前窗口大小
window.on_resized += []( Size size ){
}( window::Argument::size );

// 这个处理器需要窗口标题
window.on_resized += []( String caption ){
}( Window::Argument::text );

// 这个处理器都需要
window.on_resized += []( Size size,String caption ){
}( window::Argument::size,Window::Argument::text );

我正在写的就是这么一个框架,上面列举的消息机制都完成了,正在进行博主所指第4步封装控件和第5步(写文档,作为一个目标是开源的框架,是最重要的步骤),目前代码已经有两万多行,消息机制的其实占很少部分,大多代码是控件的封装。

re: 软件开源很重要吗? cexer 2010-09-21 22:24
我觉得软件开源没有合不合适和值不值得,只有愿不愿意。同时,不管开源如何地重要,开源与否都只是一种权利,不是义务,除非谁写的代码已经牛到了使人类科技跳跃发展二十年的程度,他不开源人类就要被外星人消灭。

但是真正要问一下,软件开源重要吗?我想没人会给出否定的答案。如果没有开源这东西,很难想像如今的软件行业还在哪个历史阶段以蜗速前进。所以作为程序员,在保证自己利益的前提下进行开源,于人于已都是最好的。蜘蛛侠里有句话:“有多大的能力,就承担多大的责任”。

话又说回来,那些不能满足自己欲望就攻击别人的人,有不如人的,有得不到的,就吐口水翻白眼骂脏话的人,和低等级动物没有区别。对于这类缺乏基本教养的动物,它叫得越厉害越反而不要给它,就让它在那里出丑。要不然你给它了它会嫌你的东西不好,骂得更厉害。

网络是个大黑屋,没有道德和法律的探照灯,反而可以显出人和人的区别。按理说技术社区既没有政治立场的对立,也没有经济利益的冲突,纯粹的技术交流应该是很愉快的,去过一些国外的技术社区也基本上气氛就是这样单纯。但是神奇的中国,就连技术社区都那么地不和谐,所以说垃圾已经塞满中国社会各个角落。

@陈梓瀚(vczh)
我对这个主题相关的技术不感兴趣。之所以留言,是觉得这篇日志的标题不妥,标题说“如何设计世界上最好的”,内容里却只“举出一些需要注意的地方”,既没见“设计”,更不知“如何”,几张图倒是占了大部分的篇幅,这样的话,“我写过的一个代码行统计工具”是个更适合的标题。
没有看到你对题目中《如何....》的解答,只看到提出问题,没有看到提供解决方案。
re: &quot;Inception&quot; 简评 cexer 2010-09-06 10:14
确实好电影,好久没看过这么好看的电影了。不过呢,还是有些不足,雪地枪战戏虽然有了架子,可是节奏单调没有高潮,失重状态的打斗也没有打击感,隔靴骚痒一样,我觉得这两个地方可以更精彩。配乐单薄,电影过后完全没有配乐的回味,应该找 Hans Zimmer 来配乐(竟然还真是他配的)。

很喜欢电影的结尾。最后从最底层梦中醒来的过程,导演故意处理没有过渡,直接简单得让人难以相信。醒来后的一切又是那么朦胧,周围人的眼神,两个孩子的穿着和动作,留给观众一个大悬念:陀螺到底会不会停下来?
很多人先有骂人的欲望,再找骂人的理由,所谓欲骂之人,何患无罪,跟几流学校什么的无关。楼主不必和这类人一般见识,有一句话说得好,不要和SB讲道理,因为他会把你的智商拉到和他同一水平,然后以多年的SB经验打败你。

最近老听说起造轮子。看看开发技术的发展史,只有造轮子才是技术发展的唯一道路。只是中国造轮子的人虽多,可是造大轮子的人太少,小打小闹的功能类太多,能大规模复用的框架太少。至于那些批判造轮子的人,忍受了东拼西凑,偷这抄那的道德谴责,忍受了接口参差,代码混乱的身心煎熬,最终能够完成项目,虽然也是一种勇气,但不知道他们批判别人的优越感来自哪里。

关于首页原创创精华。CPPBLOG是一个不知进取的博客园,n年了界面和功能从未有一点的进步,界面难看不说了,作为一个技术社区,连首页推荐模块都没有,回复功能之弱让vckbase惊叹,使CSDN侧目,所谓首页的原创精华,有无私的分享,也有自大的炫耀,有原创的思考,也有盲目的转载,随便什么人写了两行脚本,完成了一个家庭作业,都可以放到首页原创精华区去,整个博客园无管理无组织,也让人没有写东西的欲望。

@白云哥

[[[[“Because vectors keep an array format, erasing on positions other than the vector end also moves all the elements after the segment erased to their new positions”

“This invalidates all iterator and references to elements after position or first.”

删除对象后会让迭代器之后的对象移动,因此而导致迭代器失效


map的实现,红黑树,在插入和删除对象后因为要做平衡,所以同样也有可能导致迭代器的失效]]]]

就这两条原因,不会使迭代器失效,我所说的迭代器失效,是指容纳它数据结构的内存不存在。对于连续内存的容器,删除进行元素移位,迭代器的内存还在,对于非连续内存的操作,map,list,set之类的都链式的实现,这类的结点销毁的应该只是链当前结点,对其它结点的只有修改,所以不应该失效。至于2010为什么失效,有点好奇,删除操作也要内存重分配,我觉得这是不好的实现。
可以结合两种方法。删除用标记缓存删除的方法,添加用缓存延迟添加的方法好。
@白云哥
呵呵,学习了,看来还是你的办法稳当。这确实是标准库的实现相关的,我上面说的也都是基于估计它的一般实现,很好奇2010的vector是怎么样实现的,会出现这种失效的问题。
@白云哥
你看到只删除了一半,是因为对于vector这种容器,移除前面元素,后面的元素会整体往移一位,本来指向下一个元素的迭代器指向了下下个元素,后面的循环会漏掉下一个元素,这个我在上面也说过的。对于list,set,map之类的就能完全删除。需要从中间删除的容器,最好不要用vector,元素移位的操作不是常数时间的。
@白云哥
容器循环中的操作有四种,循环中查询,循环中更改,循环中删除,循环中添加,这些操作都围绕迭代器进行的。
循环中查询,循环中更改(这两类操作其实也包含了循环中循环,循环中的循环中的循环,循环中的循环中的循环中的循环。。。)因为不会改变迭代器的合法性,不会有什么问题。

循环中添加和删除则不一样,循环中添加有可能导致所有元素内存重分配,导致所有迭代器失效,而删除操作一般来说不会产生内存重分配的情况,目前std内的容器应该都是如此(七分分析三分猜测,正确性八九不离十)。

所以循环中删除只会使当前删除操作的迭代器失效,使得不能更新之使其指向下一个元素,而不会影其它迭代器。如list这类的容器,所有元素内存都是单独分配的,针对添加操作进行一次单独的内存分配,不会影响到现在有迭代器的合法性 ,但有可能删除操作导致元素重排,元素移位,使得循环不完全,漏掉元素。

循环中添加如果导致内存重分配,则会使所有现有的迭代器失效,例如vector,string这类容器要保证其内存是连续的,添加时内存有可能会重分配,导致以前的迭代器全部失效。

所以针对循环中添加和删除的几种解决方法分析如下:
在循环中保存下一个迭代器的方法:对于删除操作是没有任何问题的(关于你说的“删除容器对象有可能使得之前所保存的迭代器全都失效”,目前std内的容器应该都不会发生这种情况),但对于添加操作,则只适用于非连续内存的容器如list,不适用于连续内存的容器如vector。这个办法优点是没有时间空间的成本,缺点是对于添加操作,有些容器不支持。

使用缓存进行延迟操作的方法:对于删除和添加都适用,只是需要在所有接口中小心管理m_is_looping,避免重入的问题。优点是添加和删除都适用,缺点是空间时间的效率损失最大。

使用删除标志延迟删除的方法:对于删除操作适用,也不存在其它地方删除会出问题的情况,因为这个方法实际上是把循环中删除的操作转化为一个循环中更改的操作,所有迭代器都不会失效。即使删除操作会导致容器内存重分配,这个办法也可行,这是其优点,缺点是不能用于循环中添加的操作。
博主弄复杂了,这种方法是治标不治本的,如果容器中装的是拷贝删成本很大的东西,这样效率就低了。循环中删的问题在于删除操作之后,当前迭代器已经失效,因此无法更新之使其指向容器中的下一个元素。强行对迭代器进行 ++ 操作更新,会出现访问异常。所以只要删除之前,用一个额外的迭代器记住下一个元素的位置就行了。
如下:
//////////////////////////////////////////

iterator it_this = containter.begin();
iterator it_next;
while ( it_this != container.end() )
{
    it_next = it_this; // 之所以不直接使用 it_next=it_this+1
    ++ it_next;      // 是因为有些容器不支持随机访问,其迭代器不能直接 + n
    do_remove( it_this );
    it_this = it_next;
}

/////////////////////////////////////////////////////

楼上的“僵尸状态”也是一个办法,不过正如它的名字一样的,“僵尸”是个恶心的存在。程序在某一次循环中,把一堆元素标记为僵尸,这一堆东西必须要等到下一次循环检查才能清理掉,如果没有专门的定时清理机制,这个下一次有可能十分之一柱香之后,也有可能是一万年,甚至有可能程序流程再也没有这样的下一次。在被清理掉之前那一堆僵尸在容器里腐烂发臭,占用空间内存,可能直到最后容器销毁。可以增加专门定时清理机制,但是复杂度和成本又得另外计算。所以最好的办法还是就地处决,并且毁尸灭迹。


见过的最强的的布局管理器是WOW的。布局管理器一般实现得都灵活性不高,使用起来的代码不比手写MoveWindow少多少,能实现的布局也太有限。写的好的像WOW那种的确实很强大,不过实现起来太费精力了,使用起来也不如MoveWindow直观,反正我自己写GUI框架是不写这个的。
这种人哪里都有的,楼主息怒。
@OwnWaterloo
我说的可能不大清楚,我的意思是“单件模式”只是一种概念,正是类似“全局数据”这种东西的一种抽象形容,不然何以称“模式”。拿“单件模式”和“全局数据”本身比,是没有可比性的,一为抽象,一个具体,一为模式,一为实例。它跟OO也没有关系,一个C全局裸指针,也是一份单件的实体。所以不要再比来比去的了。

把单件放在其它模式堆里,确实有点鸡立鹤群的感觉,它并没有其它模式那样有启发性,所有写程序的人都知道世上有全局变量这个东西,那何必要取个好听的名字像模像样地研究一番呢,《设计模式》的伟大除了让人学会到很多手法,还有一点就是统一了很多概念,并为之提供了许多标准的名词方便大家研究,综上所述,我觉得“单件”就是一个虚名,你喜欢叫它“全局变量”也行,大家心里的概念没有差别,只是未必真是用全局变量来实现而已,所以不用在一个名字上纠结。

谁不想用赤祼祼的全局指针,它们原始又纯朴,天然无雕饰。可是真的要用它们来实现一份库级别的代码,就好像双手抛飞刀一样危险,不仅抛的人要两眼圆睁神经紧绷,连观众都要跟着捏一把冷汗。全局变量本来没有错,错的是在它们之上作出的许多美好的假设,假设谁一定在谁之前产生,谁一定在谁之后销毁,谁依赖着谁,谁又被谁依赖着,谁是线程安全的,谁又一定不会被多线程访问。互相完全没有依赖关系,不用额外控制,像终结者一样智能,完成任务又知道自己终结的全局变量,只是个传说。

有很多以单件名字的手法,Meyers 单件,Alexandrescu 单件,Phoenix 单件,Schwarz 计数器,你看它们好多都叫做“单件”,但是你喜欢叫 Meyers 全局对象,Alexandrescu 全局对象,Phoenix 全局对象 也可以。

我个人觉得,把“全局变量”拿来和“单件”相提并论是很不妥的事件。就好像拿“汽车”和“交通工具”比一样。不是一个层面上的东西,单件也许是全局变量,也可以不是,“全局”也有多种理解角度,存储位置(堆上,栈上,静态数据区),生存区域(线程局部,进程局部,模块内部),生存周期(何时生,何时死,不死)。只是“全局变量”已经不能说明它的复杂度,但是一提“单件”大家都明白将要说的是什么,这就是“单件”存在的价值,这也是《设计模式》一书提出“单件模式”伟大所在。
re: GUI框架:消息检查者 cexer 2009-12-01 10:10
@lch
这个我同意。不过到后来的丰富控件,已经基本上是体力活了。

re: GUI框架:消息检查者 cexer 2009-11-26 17:28
@lch
一个是手段,一个是目的,何来重要和更重要之说。
re: ffmpeg小试 cexer 2009-11-24 17:47
最近开始研究 ffmpeg,关注一下博主。
re: GUI框架:消息检查者 cexer 2009-11-24 10:38
【你那个MessageChecker怎么完成以一己之身完成,众多不同功能的?
同样可以应用到message_checker 上,两者是相通的,都是靠结构体中嵌的一个函数指针。】
我的意思是函数本身不容易实现,只用函数主要有数据保存的问题。加上 function 来实现就容易了,直接绑定一些检查函数和检查的数据:
    typedef boost::function<bool (const Message&)> MessageChecker

    bool checkCommand( const Message& message,WORD id,WORD code );
    MessageChecker checkYesClicked = boost::bind( &checkCommand,_1,IDYES,BN_CLICKED );

    bool checkMessage( const Message& message,UINT messageId, );
    MessageChecker checkCreated = boost::bind( &checkMessage,_1,WM_CREATE );

【其实我是被这段语法吸引的:
    window.onCreated += messageHandler( &::_handleCreated );
    window.onCreated += messageHandler ( this,&Window::_handleCreated );
很像boost.signal。
而且真正吸引人的是楼主说它是廉价工人~_~】
+= 之前的和之后的是两个不同的东西,onCreated 是帮助消息映射的一个东西,其实就是一个转发调用,所以成本很低。messageHandler() 是消息处理者和消息分解者共同组成的东西。在这里 ::_handleCreated 和 Window::_handleCreated 参数列表可以是不同的,这里和 boost.signal 不大一样,因为一个 signal 只能对应一种 signautre 的 functor 的。

【boost.function如果不用支持bind,可能可以不动用动态存储。
要支持bind…… 而且不使用动态存储…… 好像不行……
boost.signal肯定是要动用动态存储的。】
嗯。主要是管理起来很容易了,任何的参数直接绑定进去就行了,不用自己弄个堆对象来保存,然后还要记得删除。

re: GUI框架:消息检查者 cexer 2009-11-23 20:09
@陈梓瀚(vczh)
【function的好处就是你可以使用子类的同时不用管delete啊】
想了下用 tr1::function 来实现确实要比手写类简单得多,函数和参数绑定功能在这里很有用处,有多少参数都不用搞个多态的 Data 了,也不用自己去 delete 。确实很强大。

re: GUI框架:消息检查者 cexer 2009-11-23 17:31
@陈梓瀚(vczh)
【要坚定不移地使用这些东西。 使用tr1::function<bool(Message&)>,毫无管理复杂性,几乎没有效率成本。】
把 MessageChecker::isOk(const Message&) 的实现放到 bool MessageChecker::operator()(const Message&) 中去,函数体MessageChecker 就变成了和 function 类似的东西,但这样又有什么本质区别呢。tr1::function 的功能虽然强大,用它去实现消息检查者,该写的逻辑还得写,省不了功夫,扩展性也是个问题。

【至于什么是框架什么是类库,有一个很容易的判断标准:template method就是框架。】
谢谢,很多书对这个也言之不详,只是叫着爽就行了,所以我自己也就不大明白。我从此以后照你这个标准去评判好了。

【而且,千万不要去考虑标准库里面什么类的性能如何的问题,你永远假定他们是用魔法完成的,不需要任何CPU周期。不然你一行代码都写不出来。】
我是从不考虑那些的:标准库的效率再差,也不会到我放弃使用它们的地步的。另外以我的水平写出来的东西绝对比它的慢,哪有资格嫌弃人家。

【但是实际上windows的消息很乱,事件跟消息并不是一一对上号的。因此MessageChecker之类的东西最终用户是看不到的,你要根据每一个控件的具体情况,重新整理出一系列事件,才能让用的时候真正爽快起来。这里没有技术问题。】
你说得对,框架用户是看不到 MessageChecker 的,他们看到的是 onCreated,onClosed,onClicked 之类的东西。

re: GUI框架:消息检查者 cexer 2009-11-23 16:57
@陈梓瀚(vczh)
【继续搅局:你凭什么认为MessageChecker父类的成员变量一定够用呢?如果够用的话,就证明你的逻辑已经是固定的了,为什么要子类?就一个MessageChecker好了,不用继承。你矛盾了。】
欢迎搅局!你提出一问题确实有“以子之矛攻子之盾”的杀伤力,不过幸好我不是既卖矛又卖盾的。关于数据不够用的情况 OwnWaterloo 的也提出过,你可以倒着往上看给 OwnWaterloo 的回复,在 MessageChecker 当中加一个多态的 Data 成员可以放下所有东西,我还是把这东西更新到博文里去吧免得又有人问。“逻辑”的关键显然不是数据,而是那个检查的动作。就像银行存了五千万,不拿出去挥霍也住不上豪宅一样,成员变量够用成员函数不够用也是白搭,必须得要改写。要想一个类,不继承,不改写,而要满足不断出现的需求,这肯定是不能完成的任务。“数据够用”和“函数改写”并矛盾。

【类的灵活性在于,你使用shared_ptr<Base>保存了一个Derived*,然后调用虚函数。代价非常低,我经常把智能指针放进容器,也可以不管谁去释放,总之会被释放。而且我这种写compiler的人对循环引用十分敏感所以我基本不会犯这种错误……】
你是建议我把 MessageChecker 在堆上生成用智能指针管理起来吧。我也觉得这样确实可以使用真正强大的虚函数,但这样要付出每次都在堆上构造的效率成本和管理上的复杂性,而实际上大多数情况下这样的付出是不必的:在 Windows 的消息机制下,消息检查大多数时候需要的数据是很少的,用 WPARAM 和 LPARAM 就可以装下,但确实有需要在堆上保存数据的情况,所以我在 MessgaeChecker 增加了一个多态的 Data 成员来保存,它只有极少时候的才会从堆上生成,这样尽量避免了堆上生成的复杂性和效率损失,而完成的功能又丝毫不减。

re: GUI框架:消息检查者 cexer 2009-11-23 10:17
@OwnWaterloo
【框架定义了轮廓线,由程序员去填充颜色。类库提供调色板等工具,由程序员去绘制。】
这句话比喻得很好。你把框架理解成一种束缚是觉得需要按照它规定的方式去使用,但类库甚至 API 何尝不是也有自己的规则。我理解的框架和类库其基本目的都是一样的:对繁琐的细节进行封装,提供更为便利的接口。这当中肯定会损失灵活性,毕竟鱼和熊掌很难兼得。但我觉得框架其实是类库的进化后的身份,从字面意义上来讲,“框架”看起来更有弹性,扩展的意思。但实际上大家对这两个词的概念并没有很明白的分辨,比如说到 MFC,有人说框架有人说类库大家的感觉都是一样的。

【这个,其实也是反的……OO是思想,class只是它的一种实现方式。也许使用会比较方便。但灵活性是不如函数+数据的。】
我的想法相反。你说类的灵活性不如函数加数据,但类难道不正是建立在函数和数据之上的一个超强结合体?之所以用C之类的 OP 语言实现模式不如C++这样的 OO 语言容易,一大原因正是它缺少类的支持。

【message_checker ButtonClickingChecker(WORD id);
message_checker xxxChecker( ... );】
这是你举例说明的用函数来实现检查者。你可以尝试真的用函数来实现消息检查者,这个 ButtonClickingChecker(WORD id) , xxxChecker( ... ) 函数内部你各自要怎么实现?它既要包含不同的数据,又要包含不同的操作,它们返回完全相同的 message_checker 对象,这个 message_checker 又要怎么实现才能以一已之身完成众多不同的功能?由于不同参数列表的函数实际上是完全不同的东西,你甚至不能以统一的方式保存它们管理它们。

re: GUI框架:消息检查者 cexer 2009-11-22 21:21
cppblg 这烂程序,吃空格太严重了。

re: GUI框架:消息检查者 cexer 2009-11-22 21:05
【不过我想了一个例子,比如我要在xxx-yyy时间段内,检查这个消息,返回真,这个时间段外,就返回假。
这个checker就做不到了。 肯定要放到其他地方做,比如hanlder中。】
是可以的啊,可以像下面这样实现。
    class TimeMessageChecker:public MessageChecker
    {
    public:
        TimeMessageChecker( timeStart,timeTo )
            :MessageChecker( 0,timeStart,timeTo,&TimeMessageChecker:virtualIsOk )
        {}
    protected:
        static bool virtualIsOk( const MessageChecker* pthis,const Message& )
        {
            timeNow   = GetCurrentTime();
            timeStart = pthis->wparam;
            timeTo    = pthis->lparam;
            return timeNow>=timeStart && timeNow<=timeTo;
        }
    }

可能你的意思是遇到消息参数装不下检查时需要的信息的情况应该怎么办,这里举例说明一下怎么实现。比如说要检查是否某个字符串,这样的实现可以装下任何需要的信息。
先定义一个多态的数据类型,为了效率这里可以使用引用计数之类的东西
    class MessageData
    {
    public:
        virtual ~MessageData()
        {}
        virtual void release()
        {
            delete this;
        }
        virtual MessageData* clone() const = 0;
    };

MessageChecker 当中有这个成员
    class MessageChecker
    {
    //......
    public:
        MessageData* data;
    }

修改拷贝构造函数和析构函数
    MessageChecker::MessageChecker( const MessageChecker& other )
    {
        if ( data )
        {
            data->release();
            data = NULL;
        }
        if ( other.data )
        {
            data = other.data->clone();
        }
    }

    MessageChecker::~MessageChecker()
    {
        if ( data )
        {
            data->release();
            data = NULL;
        }
    }

定义一个装字符串的多态数据类
    class StringMessageData:public MessageData
    {
    public:
        StringMessageData( const String& str_ )
            :string( str_ )
        {}
        virtual MessageData* clone() const
        {
            return new StringMessageData(string);
        }
    public:
        String  string;
    };

利用数据成员 data 所装的信息可以检查字符串的消息检查者
    class StringMessageChecker:public MessageChecker
    {
    public:
        StringMessageChecker( const String& string )
            :MessageChecker( 0,0,0,&StringMessageChecker::virutalIsOk )
        {
            data = new StringMessageData( string );
        }
    public:
        static bool virtualIsOk( const MessageChecker* pthis,const Message& message )
        {
            StringMessageData* data = (StringMessageData*)pthis->data;
            if ( !data )
            {
                return false;
            }
            std::string stirngToCheck = (const Char*)( message.wparam );
            return stirngToCheck == data->string;
        }
    };

【对,这就是我对"框架"感到反感的地方之一 —— 它"限制"你做事的方法。不对它熟悉到一定程度,就没法工作……
作为程序员……我也了解用户的需求是很恶心的…… 就像你有一篇blog里写的那样……所以,推己及人……对我提出的这个恶心的需求,听过之后就当是废话好了~_~】
说实话我一直对“框架”和“类库”的概念分不大清楚,我想“框架”从名字上来说多了一个“可以扩展,可以填充”的意思,没有你说的“限制你做事的方法”这种感觉。还有你提到了 GUI 框架用户的需求。要说明一下的是,实现的这个消息检查者只是在框架内工作的,方便 GUI 框架的开发者和扩充者使用,GUI 框架的使用者不会接触到这个东西。最终的用户只需要使用是像这个样子:
    window.onCreated += messageHandler( &::_handleCreated );
    window.onCreated += messageHandler ( this,&Window::_handleCreated );

共4页: 1 2 3 4