应用篇_撤销(Undo)和重做(Redo)的C++自动化实现(6)---扩展的框架代码

本文详细介绍了如何在C++中扩展撤销(Undo)和重做(Redo)的命令模式框架,通过基本命令类`basic_command`,允许添加额外信息如名称,以适应图形用户界面的需求。此外,文章提出了解决无法使用默认构造函数创建对象的容器类,以及配置类`basic_config`,增强了命令中心的可扩展性。文章提供了多个扩展命令的测试用例,以展示其功能。
摘要由CSDN通过智能技术生成
作者: 熊春雷
网站: http://www.autodev.net
Blog: http://blog.csdn.net/pandaxcl
EMail: pandaxcl@163.com
QQ: 56637059
版本: 0.01 于2007/09/25
目标: 所有C++爱好者
版权: 本文的版权归熊春雷所有

Warning

  1. 本文由熊春雷所写,绝对保证原创,在此特别严肃声明。

  2. 绝对不能容忍他人说本文为他所写以及其他的侵权行为。一旦发现,一定 尽本人最大的能力以法律的形式严追到底,决不妥协。

  3. 引用本文,要保证本文的完整性,不可以删除此处的声明,并且务必注明出处。

Tip

  1. 本文编写的所有代码可以用于任何用途(包括商业用途)。

  2. 用于商业用途的需要在最后发布的软件中声明借鉴了本文的思想。具体事 宜可以协商解决,(代码决不收取任何费用)。

  3. 其他事项可以和我联系,包括技术讨论等等:)或者直接登陆网站论坛: http://www.autodev.net

Note

  1. 本文受到了《C++设计新思维》和《产生式编程》两本书的影响,同时也查阅了大 量的资料,从Loki库和Boost库中也吸收了不少营养,特此感谢之。

  2. 本文由于处于原创阶段,难免会出现各种各样的错误。代码出现错误的可能性非常 小(本来想说为零的),因为文档和代码是严格同步的,这是由VST文本的include 所保证的,代码都是测试成功之后才发布的。

  3. 本文所编写的代码,经过了VC2005编译器和g++编译器的测试,并且都通过了。

  4. 本文还没有彻底完成,算是一个初级版本,未来还将继续完善。暂时发布出来是为 了预知读者群有多少,读者越多,我的成就感越强,写作的时候也会更有动力:)

  5. 本文还会继续完善,欢迎各位读者的批评指正,也接受各种各样的建议,在权衡之 后以决定是否加入本书。

  6. 本书还没有最终完成,还会不断的进行完善,更新之后的内容将会发表于我的 网站我的博客。所以还需要读者多多关心本文的进展:)

Warning

  • 本文的有效篇幅仅仅局限于基础篇,之后的文档还仅仅只是一种创意记录,

  • 不保证正确性,特此提醒。

Contents

扩展的框架代码

有了前面的这些基础性的代码之后,我们就可以处理大量的撤销和重做操作了:)但是通 常的情况下,我们还是希望能够往command里面添加一些额外的信息。

例如:为了在图形用户界面应用程序里面显示还可以撤销的命令队列列表以及可以重做的 命令列表,这两种情况下都需要给每一个命令添加一个额外的名称信息,所以我们将前面 的command代码修改为下面的basic_command代码:

 class DefaultExtraData{};// 默认的额外信息
 template<class ExtraData=DefaultExtraData>
 struct basic_command :public ExtraData // 扩展了command类
 {
     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];
     }
     // 下面两个函数是在命令的创建和删除的时候调用的
     // 配合_unused就是一个受管理的引用计数智能指针
     // 只是引用该对象的“智能指针”只能是命令
     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;
         // 如果_unused中有引用计数为零的对象,直接返回该指针
         for( it = _unused.begin(); it != _unused.end(); ++it )
         {
             if(0 == it->second && used != it->first)
             {
                 ptr = it->first;
                 break;
             }
         }
         // 如果_unused中没有引用计数为零的对象,在_objects中新建一个对象
         // 并返回该新建对象的指针
         if( used == ptr )
         {
             // Create避免了某些对象不可以通过默认构造函数创建的问题
             _objects.push_back(create_type()());// 调用Create仿函数创建对象
             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)参数,配置参数中主要是创建仿函数和修改仿函数 !分别解决下面的问题:

  1. 创建仿函数(create)。主要用来处理那些默认构造函数不是public属性的类 ,创建这些对象就只能通过其他的构造函数了!

  2. 修改仿函数(modify)。主要用来处理那些不可以通过赋值运算符修改对象属 性的类,修改这些对象就只能通过其他的方法了!

有了上面的两个模板参数之后就可以使得basic_container类具有极大的可扩展性和处理能 力。下面是容器类需要的配置类:

 template<class T>class basic_config
 {
 protected:
     struct Create
     {
         const T&operator()()
         {
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值