Warning
-
本文由熊春雷所写,绝对保证原创,在此特别严肃声明。
-
绝对不能容忍他人说本文为他所写以及其他的侵权行为。一旦发现,一定 尽本人最大的能力以法律的形式严追到底,决不妥协。
-
引用本文,要保证本文的完整性,不可以删除此处的声明,并且务必注明出处。
Tip
-
本文编写的所有代码可以用于任何用途(包括商业用途)。
-
用于商业用途的需要在最后发布的软件中声明借鉴了本文的思想。具体事 宜可以协商解决,(代码决不收取任何费用)。
-
其他事项可以和我联系,包括技术讨论等等:)或者直接登陆网站论坛: http://www.autodev.net
Note
-
本文受到了《C++设计新思维》和《产生式编程》两本书的影响,同时也查阅了大 量的资料,从Loki库和Boost库中也吸收了不少营养,特此感谢之。
-
本文由于处于原创阶段,难免会出现各种各样的错误。代码出现错误的可能性非常 小(本来想说为零的),因为文档和代码是严格同步的,这是由VST文本的include 所保证的,代码都是测试成功之后才发布的。
-
本文所编写的代码,经过了VC2005编译器和g++编译器的测试,并且都通过了。
-
本文还没有彻底完成,算是一个初级版本,未来还将继续完善。暂时发布出来是为 了预知读者群有多少,读者越多,我的成就感越强,写作的时候也会更有动力:)
-
本文还会继续完善,欢迎各位读者的批评指正,也接受各种各样的建议,在权衡之 后以决定是否加入本书。
-
本书还没有最终完成,还会不断的进行完善,更新之后的内容将会发表于我的 网站或我的博客。所以还需要读者多多关心本文的进展:)
有了前面的这些基础性的代码之后,我们就可以处理大量的撤销和重做操作了:)但是通 常的情况下,我们还是希望能够往command里面添加一些额外的信息。
例如:为了在图形用户界面应用程序里面显示还可以撤销的命令队列列表以及可以重做的 命令列表,这两种情况下都需要给每一个命令添加一个额外的名称信息,所以我们将前面 的command代码修改为下面的basic_command代码:
class DefaultExtraData{};
template<class ExtraData=DefaultExtraData>
struct basic_command :public ExtraData
{
virtual ~basic_command(){}
virtual void redo()=0;
virtual void undo()=0;
};
template<> struct basic_command<DefaultExtraData>
{
virtual ~basic_command(){}
virtual void redo()=0;
virtual void undo()=0;
};
特别注意上面的代码,为了避免空基类,采用了模板特化的静态选择机制 ,这样就可以根据模板参数选择合适的command类。
从上面的代码还可以看出,提供一个ExtraData给basic_command就可以完全的改变前面提 供的command的结构!可以增加属性,也可以增加方法。总之所有可以添加的信息都可以, 现在给出一个示例:
struct MyExtraData
{
std::string name;
};
有了这个额外的数据之后,就可以采用这一系列的basic_nx模板类来使得命令类可以添加 额外的属性信息了;)当然仅仅限于basic_command模板类及其派生类才有这个功能, command仍然没有这个功能!
除了上面的给命令添加额外的信息需求之外,还有一个比较常见的问题会在应用 中出现:容器类中盛放的对象类型的默认构造函数不能调用的时候,前面的 container类就无能为力了!所以在此也要给出一种处理这种情况的能力!除此之外,前面 还假定:该容器类里面的所有对象的修改都是通过对象类的赋值运算符实现的, 这在大多数情况下是可以接受的,但是这样毕竟还是一个限制,而且这是不必要的!
可以处理非默认构造函数创建对象的容器类:
template<class T,class Config=basic_config<T> > class basic_container :public std::set<T*>
{
public:
typedef T object_type;
typedef std::list<T> objects_type;
typedef std::map<T*,int> unused_type;
typedef typename Config::create_type create_type;
typedef typename Config::modify_type modify_type;
private:
objects_type _objects;
unused_type _unused;
public:
typename unused_type::mapped_type used(T**pID)
{
return _unused.find(*pID)==_unused.end()?0:_unused[*pID];
}
void increase(T*ID){ _unused[ID]++; }
void decrease(T*ID){ _unused[ID]--; }
T*generate(T* used = NULL)
{
T* ptr = used;
typename unused_type::iterator it;
for( it = _unused.begin(); it != _unused.end(); ++it )
{
if(0 == it->second && used != it->first)
{
ptr = it->first;
break;
}
}
if( used == ptr )
{
_objects.push_back(create_type()());
ptr = &_objects.back();
}
return ptr;
}
public:
void create(T*ID)
{
this->insert(ID);
}
void modify(T*ID,const T&v)
{
modify_type()(*ID,v);
}
void remove(T*ID)
{
this->erase(ID);
}
};
容器类增加了一个配置(basic_config)参数,配置参数中主要是创建仿函数和修改仿函数 !分别解决下面的问题:
-
创建仿函数(create)。主要用来处理那些默认构造函数不是public属性的类 ,创建这些对象就只能通过其他的构造函数了!
-
修改仿函数(modify)。主要用来处理那些不可以通过赋值运算符修改对象属 性的类,修改这些对象就只能通过其他的方法了!
有了上面的两个模板参数之后就可以使得basic_container类具有极大的可扩展性和处理能 力。下面是容器类需要的配置类:
template<class T>class basic_config
{
protected:
struct Create
{
const T&operator()()
{
static const T O;
return O;
}
};
struct Modify
{
void operator()(T&lhs,const T&rhs)
{
lhs = rhs;
}
};
public:
typedef Create create_type;
typedef Modify modify_type;
};
有了上面的这种容器类之后就可以处理创建命令特别常用的一种功能:创建对象的时候能 够提供创建参数,在创建对象的时候就对对象进行修改,只是这种修改是不可以撤销的! 可以添加额外信息并且可以提供创建参数的创建命令:
template<class Container,class Command> class basic_create :public Command
{
void initialize()
{
_ID = _C.generate(_ID);
_C.increase(_ID);
}
typedef typename Container::object_type T;
typedef typename Container::modify_type M;
public:
basic_create(Container&C,T*&ID):_C(C),_ID(ID)
{
initialize();
}
basic_create(Container&C,T*&ID,const T&O):_C(C),_ID(ID)
{
initialize(); M()(*_ID , O);
}
virtual~basic_create()
{
_C.decrease(_ID);
}
void redo()
{
_C.create(_ID);
}
void undo()
{
_C.remove(_ID);
}
private:
basic_create(){}
Container &_C;
T*& _ID;
};
可以添加额外信息的修改命令:
template<class Container,class Command> class basic_modify :public Command
{
typedef typename Container::object_type T;
typedef typename Container::modify_type M;
public:
basic_modify(Container&C,T*ID,const T&O):_C(C),_ID(ID),_O(O)
{
M()(_OB , *_ID);
_C.increase(_ID);
}
virtual~basic_modify()
{
_C.decrease(_ID);
}
void redo()
{
_C.modify(_ID,_O);
}
void undo()
{
_C.modify(_ID,_OB);
}
private:
basic_modify(){}
Container &_C;
T*_ID;
T _O ;
T _OB;
};
可以添加额外信息的删除命令:
template<class Container,class Command> class basic_remove :public Command
{
typedef typename Container::object_type T;
public:
basic_remove(Container&C,T*&ID):_C(C),_ID(ID),_IDB(ID)
{
_C.increase(_ID);
}
virtual~basic_remove()
{
_C.decrease(_ID);
}
void redo()
{
_C.remove(_ID);
_ID = NULL;
}
void undo()
{
_ID = _IDB;
_C.create(_ID);
}
private:
basic_remove(){}
Container &_C ;
T*&_ID;
T* _IDB;
};
下面是三个基本命令的扩展代码的测试用例:
typedef undo::basic_container<std::string> CT;
typedef undo::basic_command<MyExtraData> Command;
typedef undo::basic_create<CT,Command> Create;
typedef undo::basic_modify<CT,Command> Modify;
typedef undo::basic_remove<CT,Command> Remove;
CT c;
CT::object_type* PANDAXCL = NULL;
AUTOCXX_ASSERT_EQUAL(c.used(&PANDAXCL),0);
Create createPandaxcl(c,PANDAXCL,std::string("熊春雷"));
AUTOCXX_ASSERT_EQUAL(c.size(),0);
AUTOCXX_ASSERT_EQUAL(c.used(&PANDAXCL),1);
AUTOCXX_ASSERT_NOT_EQUAL(PANDAXCL,NULL);
createPandaxcl.redo();
AUTOCXX_ASSERT_NOT_EQUAL(PANDAXCL,NULL);
AUTOCXX_ASSERT_EQUAL(c.size(),1);
AUTOCXX_ASSERT_EQUAL(c.used(&PANDAXCL),1);
createPandaxcl.undo();
AUTOCXX_ASSERT_NOT_EQUAL(PANDAXCL,NULL);
AUTOCXX_ASSERT_EQUAL(c.size(),0);
AUTOCXX_ASSERT_EQUAL(c.used(&PANDAXCL),1);
createPandaxcl.redo();
AUTOCXX_ASSERT_EQUAL(c.size(),1);
AUTOCXX_ASSERT_EQUAL(c.used(&PANDAXCL),1);
AUTOCXX_ASSERT_EQUAL(c.used(&PANDAXCL),1);
Modify modifyPandaxcl(c,PANDAXCL,std::string("PANDAXCL"));
AUTOCXX_ASSERT_EQUAL(c.used(&PANDAXCL),2);
modifyPandaxcl.redo();
AUTOCXX_ASSERT_EQUAL(c.size(),1);
AUTOCXX_ASSERT_EQUAL(c.used(&PANDAXCL),2);
AUTOCXX_ASSERT_NOT_EQUAL(*PANDAXCL,std::string("熊春雷"));
AUTOCXX_ASSERT_EQUAL(*PANDAXCL,std::string("PANDAXCL"));
modifyPandaxcl.undo();
AUTOCXX_ASSERT_EQUAL(c.size(),1);
AUTOCXX_ASSERT_EQUAL(c.used(&PANDAXCL),2);
AUTOCXX_ASSERT_EQUAL(*PANDAXCL,std::string("熊春雷"));
AUTOCXX_ASSERT_NOT_EQUAL(*PANDAXCL,std::string("PANDAXCL"));
modifyPandaxcl.redo();
AUTOCXX_ASSERT_EQUAL(c.size(),1);
AUTOCXX_ASSERT_EQUAL(c.used(&PANDAXCL),2);
AUTOCXX_ASSERT_NOT_EQUAL(*PANDAXCL,std::string("熊春雷"));
AUTOCXX_ASSERT_EQUAL(*PANDAXCL,std::string("PANDAXCL"));
modifyPandaxcl.undo();
AUTOCXX_ASSERT_EQUAL(c.size(),1);
AUTOCXX_ASSERT_EQUAL(c.used(&PANDAXCL),2);
AUTOCXX_ASSERT_EQUAL(*PANDAXCL,std::string("熊春雷"));
AUTOCXX_ASSERT_NOT_EQUAL(*PANDAXCL,std::string("PANDAXCL"));
Remove removePandaxcl(c,PANDAXCL);
AUTOCXX_ASSERT_EQUAL(c.used(&PANDAXCL),3);
removePandaxcl.redo();
AUTOCXX_ASSERT_EQUAL(c.size(),0);
AUTOCXX_ASSERT_EQUAL(c.used(&PANDAXCL),0);
removePandaxcl.undo();
AUTOCXX_ASSERT_EQUAL(c.size(),1);
AUTOCXX_ASSERT_EQUAL(c.used(&PANDAXCL),3);
AUTOCXX_ASSERT_EQUAL(*PANDAXCL,std::string("熊春雷"));
AUTOCXX_ASSERT_NOT_EQUAL(*PANDAXCL,std::string("PANDAXCL"));
可以添加额外信息的复合命令:
template<class Command>class basic_batch
:public Command
,public std::list<Command*>
{
typedef Command command_type;
public:
virtual~basic_batch()
{
this->free();
}
void record(command_type*pCmd)
{
this->push_back(pCmd);
}
public:
virtual void redo()
{
std::for_each(this->begin(),this->end(),std::mem_fun(&command_type::redo));
}
virtual void undo()
{
std::for_each(this->rbegin(),this->rend(),std::mem_fun(&command_type::undo));
}
void free()
{
std::for_each(this->begin(),this->end(),kill());
this->clear();
}
};
下面是复合命令的扩展代码的测试用例:
typedef undo::basic_command<MyExtraData> Command;
typedef undo::basic_container<std::string> CT;
typedef undo::basic_create<CT,Command> Create;
typedef undo::basic_modify<CT,Command> Modify;
typedef undo::basic_remove<CT,Command> Remove;
CT c;
CT::object_type* PANDAXCL = NULL;
CT::object_type* QQ = NULL;
CT::object_type* BLOG = NULL;
CT::object_type* EMAIL = NULL;
CT::object_type* NETWORK = NULL;
undo::basic_batch<Command> bat;
bat.record(new Create(c,PANDAXCL));
bat.record(new Create(c,QQ));
bat.record(new Create(c,BLOG));
bat.record(new Create(c,EMAIL));
bat.record(new Create(c,NETWORK));
AUTOCXX_ASSERT_EQUAL(c.size(),0);
AUTOCXX_ASSERT_NOT_EQUAL(PANDAXCL,NULL);
AUTOCXX_ASSERT_NOT_EQUAL(QQ ,NULL);
AUTOCXX_ASSERT_NOT_EQUAL(BLOG ,NULL);
AUTOCXX_ASSERT_NOT_EQUAL(EMAIL ,NULL);
AUTOCXX_ASSERT_NOT_EQUAL(NETWORK ,NULL);
bat.redo();
AUTOCXX_ASSERT_NOT_EQUAL(PANDAXCL,NULL);
AUTOCXX_ASSERT_NOT_EQUAL(QQ ,NULL);
AUTOCXX_ASSERT_NOT_EQUAL(BLOG ,NULL);
AUTOCXX_ASSERT_NOT_EQUAL(EMAIL ,NULL);
AUTOCXX_ASSERT_NOT_EQUAL(NETWORK ,NULL);
*PANDAXCL = std::string("熊春雷");
*QQ = std::string("56637059");
*BLOG = std::string("http://blog.csdn.net/pandaxcl");
*EMAIL = std::string("pandaxcl@163.com");
*NETWORK = std::string("http://www.autodev.net");
AUTOCXX_ASSERT_EQUAL(c.size(),5);
AUTOCXX_ASSERT_EQUAL(*PANDAXCL,std::string("熊春雷"));
AUTOCXX_ASSERT_EQUAL(*QQ ,std::string("56637059"));
AUTOCXX_ASSERT_EQUAL(*BLOG ,std::string("http://blog.csdn.net/pandaxcl"));
AUTOCXX_ASSERT_EQUAL(*EMAIL ,std::string("pandaxcl@163.com"));
AUTOCXX_ASSERT_EQUAL(*NETWORK ,std::string("http://www.autodev.net"));
bat.undo();
AUTOCXX_ASSERT_EQUAL(c.size(),0);
bat.redo();
AUTOCXX_ASSERT_EQUAL(c.size(),5);
AUTOCXX_ASSERT_EQUAL(*PANDAXCL,std::string("熊春雷"));
AUTOCXX_ASSERT_EQUAL(*QQ ,std::string("56637059"));
AUTOCXX_ASSERT_EQUAL(*BLOG ,std::string("http://blog.csdn.net/pandaxcl"));
AUTOCXX_ASSERT_EQUAL(*EMAIL ,std::string("pandaxcl@163.com"));
AUTOCXX_ASSERT_EQUAL(*NETWORK ,std::string("http://www.autodev.net"));
bat.free();
bat.record(new Modify(c,PANDAXCL,std::string("PANDAXCL")));
bat.record(new Remove(c,QQ));
undo::basic_batch<Command> * childbat = new undo::basic_batch<Command>();
childbat->record(new Modify(c,NETWORK,std::string("http://www.autocxx.net")));
childbat->record(new Modify(c,NETWORK,std::string("http://www.autodev.net")));
bat.record(childbat);
bat.redo();
AUTOCXX_ASSERT_EQUAL(c.size(),4);
AUTOCXX_ASSERT_EQUAL(*PANDAXCL,std::string("PANDAXCL"));
AUTOCXX_ASSERT_EQUAL(*BLOG,std::string("http://blog.csdn.net/pandaxcl"));
AUTOCXX_ASSERT_EQUAL(*EMAIL,std::string("pandaxcl@163.com"));
AUTOCXX_ASSERT_EQUAL(*NETWORK,std::string("http://www.autodev.net"));
bat.undo();
AUTOCXX_ASSERT_EQUAL(c.size(),5);
AUTOCXX_ASSERT_EQUAL(*PANDAXCL,std::string("熊春雷"));
AUTOCXX_ASSERT_EQUAL(*QQ,std::string("56637059"));
AUTOCXX_ASSERT_EQUAL(*BLOG,std::string("http://blog.csdn.net/pandaxcl"));
AUTOCXX_ASSERT_EQUAL(*EMAIL,std::string("pandaxcl@163.com"));
AUTOCXX_ASSERT_EQUAL(*NETWORK,std::string("http://www.autodev.net"));
bat.redo();
AUTOCXX_ASSERT_EQUAL(c.size(),4);
AUTOCXX_ASSERT_EQUAL(*PANDAXCL,std::string("PANDAXCL"));
AUTOCXX_ASSERT_EQUAL(*BLOG,std::string("http://blog.csdn.net/pandaxcl"));
AUTOCXX_ASSERT_EQUAL(*EMAIL,std::string("pandaxcl@163.com"));
AUTOCXX_ASSERT_EQUAL(*NETWORK,std::string("http://www.autodev.net"));
可以添加额外信息的命令中心:
template<class Command>class basic_undo_bat:public basic_batch<Command>{};
template<class Command>class basic_redo_bat:public basic_batch<Command>{};
template<class Command>class basic_center
:public basic_undo_bat<Command>
,public basic_redo_bat<Command>
{
private:
size_t _limit;
public:
typedef Command command_type;
typedef basic_batch <Command> batch_type;
typedef basic_redo_bat<Command> redo_type;
typedef basic_undo_bat<Command> undo_type;
typedef undo_type history_type;
basic_center():_limit(100)
{
}
virtual~basic_center()
{
}
public:
size_t limit()
{
return _limit;
}
void limit(size_t Limit)
{
_limit=Limit;
relimit();
}
void relimit()
{
typedef std::list<command_type*> QT;
if(_limit >= undo_type::size())return;
while(_limit < undo_type::size())
{
command_type *pCmd = undo_type::front();
undo_type::pop_front();
assert( NULL != pCmd );
delete pCmd;
}
}
public:
bool undoable()
{
return !undo_type::empty();
}
bool redoable()
{
return !redo_type::empty();
}
void undo()
{
if (!undo_type::empty()) {
undo_type::back()->undo();
redo_type::push_back(undo_type::back());
undo_type::pop_back();
}
}
void redo()
{
if (!redo_type::empty()) {
redo_type::back()->redo();
undo_type::push_back(redo_type::back());
redo_type::pop_back();
}
}
void undo(int number)
{
for(int i=0;i<number;i++) {
if(undo_type::empty())break;
else undo();
}
}
void redo(int number)
{
for(int i=0;i<number;i++) {
if(redo_type::empty())break;
else redo();
}
}
void record(command_type*pCmd=NULL)
{
history_type::record(pCmd);
redo_type::free();
}
void execute(command_type*pCmd)
{
assert(pCmd!=NULL);
pCmd->redo();
record(pCmd);
relimit();
}
void stop()
{
history_type &H=static_cast<history_type&>(*this);
typename history_type::iterator result=H.begin();
typename history_type::reverse_iterator rit;
rit = std::find(H.rbegin(),H.rend(),static_cast<command_type*>(NULL));
std::advance(result,std::distance(rit,H.rend())-1);
if(result!=H.end())
{
typename history_type::difference_type diff;
diff = std::distance(result,H.end());
if(diff==1 || diff==2) {
H.erase(result);
}else{
batch_type*pBat=new batch_type();
pBat->splice(pBat->begin(),H,result,H.end());
pBat->remove(static_cast<command_type*>(NULL));
record(pBat);
}
}
}
};
下面是命令中心的扩展代码的基本状态的测试用例:
typedef undo::basic_command<MyExtraData> Command;
typedef undo::basic_container<std::string> CT;
typedef undo::basic_create<CT,Command> Create;
typedef undo::basic_modify<CT,Command> Modify;
typedef undo::basic_remove<CT,Command> Remove;
CT c;
undo::basic_center<Command> cc;
CT::object_type* PANDAXCL = NULL;
CT::object_type* QQ = NULL;
CT::object_type* BLOG = NULL;
CT::object_type* EMAIL = NULL;
CT::object_type* NETWORK = NULL;
AUTOCXX_ASSERT_EQUAL(c.size(),0);
cc.execute(new Create(c,PANDAXCL));
AUTOCXX_ASSERT_EQUAL(c.size(),1);
cc.execute(new Create(c,QQ));
AUTOCXX_ASSERT_EQUAL(c.size(),2);
cc.execute(new Create(c,BLOG));
AUTOCXX_ASSERT_EQUAL(c.size(),3);
cc.execute(new Create(c,EMAIL));
AUTOCXX_ASSERT_EQUAL(c.size(),4);
cc.undo();
AUTOCXX_ASSERT_EQUAL(c.size(),3);
cc.redo();
AUTOCXX_ASSERT_EQUAL(c.size(),4);
cc.undo(3);
AUTOCXX_ASSERT_EQUAL(c.size(),1);
cc.redo(3);
AUTOCXX_ASSERT_EQUAL(c.size(),4);
cc.undo(100);
AUTOCXX_ASSERT_EQUAL(c.size(),0);
cc.redo(200);
AUTOCXX_ASSERT_EQUAL(c.size(),4);
cc.undo(3);
AUTOCXX_ASSERT_EQUAL(c.size(),1);
cc.redo(1);
AUTOCXX_ASSERT_EQUAL(c.size(),2);
cc.undo(100);
AUTOCXX_ASSERT_EQUAL(c.size(),0);
cc.redo(100);
AUTOCXX_ASSERT_EQUAL(c.size(),4);
cc.undo(1);
AUTOCXX_ASSERT_EQUAL(c.size(),3);
cc.undo(1);
AUTOCXX_ASSERT_EQUAL(c.size(),2);
cc.undo(1);
AUTOCXX_ASSERT_EQUAL(c.size(),1);
cc.redo(3);
AUTOCXX_ASSERT_EQUAL(c.size(),4);
cc.undo(2);
AUTOCXX_ASSERT_EQUAL(c.size(),2);
cc.execute(new Create(c,NETWORK));
AUTOCXX_ASSERT_EQUAL(c.size(),3);
cc.redo(100);
AUTOCXX_ASSERT_EQUAL(c.size(),3);
下面是命令中心的扩展代码的模拟复合命令的测试用例:
typedef undo::basic_command<MyExtraData> Command;
typedef undo::basic_container<std::string> CT;
typedef undo::basic_create<CT,Command> Create;
typedef undo::basic_modify<CT,Command> Modify;
typedef undo::basic_remove<CT,Command> Remove;
CT c;
undo::basic_center<Command> cc;
CT::object_type* PANDAXCL = NULL;
CT::object_type* QQ = NULL;
CT::object_type* BLOG = NULL;
CT::object_type* EMAIL = NULL;
CT::object_type* NETWORK = NULL;
AUTOCXX_ASSERT_EQUAL(c.size(),0);
cc.record();
cc.execute(new Create(c,PANDAXCL));
cc.execute(new Create(c,EMAIL));
cc.execute(new Create(c,QQ));
cc.execute(new Create(c,BLOG));
cc.execute(new Create(c,NETWORK));
cc.stop();
AUTOCXX_ASSERT_EQUAL(c.size(),5);
cc.undo();
AUTOCXX_ASSERT_EQUAL(c.size(),0);
cc.redo();
AUTOCXX_ASSERT_EQUAL(c.size(),5);
cc.record();
cc.stop();
下面是命令中心的扩展代码的模拟子复合命令的测试用例:
typedef undo::basic_command<MyExtraData> Command;
typedef undo::basic_container<std::string> CT;
typedef undo::basic_create<CT,Command> Create;
typedef undo::basic_modify<CT,Command> Modify;
typedef undo::basic_remove<CT,Command> Remove;
CT c;
undo::basic_center<Command> cc;
CT::object_type* PANDAXCL = NULL;
CT::object_type* QQ = NULL;
CT::object_type* BLOG = NULL;
CT::object_type* EMAIL = NULL;
CT::object_type* NETWORK = NULL;
AUTOCXX_ASSERT_EQUAL(c.size(),0);
cc.record();
{
cc.execute(new Create(c,PANDAXCL));
cc.record();
{
cc.execute(new Create(c,EMAIL));
cc.record();
{
cc.execute(new Create(c,QQ));
cc.execute(new Create(c,BLOG));
cc.execute(new Create(c,NETWORK));
}
cc.stop();
}
cc.stop();
}
cc.stop();
AUTOCXX_ASSERT_EQUAL(c.size(),5);
cc.undo();
AUTOCXX_ASSERT_EQUAL(c.size(),0);
cc.redo();
AUTOCXX_ASSERT_EQUAL(c.size(),5);
好了,到此为止已经成功的完成了命令的扩展,使用方法并没有多大不同:)可以放心使 用了:)
发表于 @ 2007年10月11日 21:26:00|评论(loading...)|编辑