C++链式继承

        继承,对于学习C++的每一个人来说,都不会陌生。在Qt的开发中,如果你需要对一个无边框的界面支持move操作,那么你就得通过继承重写虚函数来实现,这并不难,但如果我还需要对一个按钮支持移动,一般情况,当然是Crtl + c 、Crtl + v搞定,但我们不难发现,对于move这个操作来说,其实代码完全一模一样,那么有没有什么办法可以简化,可以一劳永逸呢?

        答案是肯定的,这里我们就需要用到C++的模板来实现了,即本文要介绍的链式继承。

        前面有实现过move操作,这里将它抽取出来,代码如下:

  1: #include <QWidget>
  2: #include <QMouseEvent>
  3: #include <QPoint>
  4:
  5: //T 为基类,继续拥有一个QWidget *parent参数的构造函数
  6: template <typename T>
  7: class WidgetMove : public T
  8: {
  9: public:
 10:     WidgetMove(QWidget *parent = 0):T(parent)
 11:     {
 12:     }
 13: protected:
 14:     void mousePressEvent(QMouseEvent *event)
 15:     {
 16:         //当鼠标左键按下时,记录当前位置
 17:         if(event->button() == Qt::LeftButton)
 18:         {
 19:             m_CurrentPos = event->globalPos() - T::frameGeometry().topLeft();
 20:             event->accept();
 21:         }
 22:         T::mousePressEvent(event);
 23:     }
 24:
 25:     void mouseMoveEvent(QMouseEvent *event)
 26:     {
 27:         //支持窗体移动
 28:         if (event->buttons() & Qt::LeftButton)
 29:         {
 30:             T::move(event->globalPos() - m_CurrentPos);
 31:             event->accept();
 32:         }
 33:         T::mouseMoveEvent(event);
 34:     }
 35: private:
 36:     QPoint m_CurrentPos;
 37: };

 

接下来我们实现一个图片按钮:

  1: #include <QPushButton>
  2: #include "widgetmove.h"
  3:
  4: //间接继承QPushButton,让按钮支持移动
  5: class ImgButton : public QPushButton
  6: {
  7:     Q_OBJECT
  8: public:
  9:     explicit ImgButton(QWidget *parent = 0);
 10:     void paintEvent(QPaintEvent *event);
 11:     QPixmap m_Pixmap;
 12: };
 13:
 14:
 15: #include "imgbutton.h"
 16: #include <QIcon>
 17: #include <QBitmap>
 18:
 19: ImgButton::ImgButton(QWidget *parent) :
 20:    QPushButton(parent)
 21: {
 22:     //必须设置为无边框,否则可见区域和图片绘制区域将出现不重叠
 23:     setWindowFlags( Qt::FramelessWindowHint );
 24:     m_Pixmap.load("close.png");
 25:     resize(100,100);
 26:     m_Pixmap = m_Pixmap.scaled(this->size(),Qt::IgnoreAspectRatio);
 27: }
 28:
 29: void ImgButton::paintEvent(QPaintEvent *event)
 30: {
 31:     //绘制背景图片
 32:     QIcon icon(m_Pixmap);
 33:     this->setIcon(icon);
 34:     this->setIconSize(size());
 35:     //将png图片透明部分设置为穿透
 36:     this->setMask(m_Pixmap.mask());
 37:     //绘制
 38:     QPushButton::paintEvent(event);
 39: }

然后我们把异形窗体重新实现了:

  1: #include <QString>
  2: #include <QBitmap>
  3: #include <QPaintEvent>
  4: #include <QPalette>
  5: //异形窗体实现
  6:
  7: //T 为基类,继续拥有一个QWidget *parent参数的构造函数
  8: template <typename T>
  9: class WidgetRuleless : public T
 10: {
 11: public:
 12:     WidgetRuleless(QWidget *parent = 0):T(parent)
 13:     {
 14:         //设置为无边框
 15:         T::setWindowFlags( Qt::FramelessWindowHint );
 16:     }
 17:     void SetBackgroundImg(const QString &imgFile)
 18:     {
 19:         m_Pixmap.load(imgFile);
 20:
 21:         //保持图片跟界面一样大小
 22:         m_Pixmap = m_Pixmap.scaled(T::size());
 23:
 24:         T::setAutoFillBackground(true);
 25:
 26:         //不规则窗口的关键,将图片透明的地方设为穿透
 27:         T::setMask( m_Pixmap.mask() );
 28:     }
 29: protected:
 30:     void paintEvent(QPaintEvent *event)
 31:     {
 32:         if(!m_Pixmap.isNull())
 33:         {
 34:             //绘制背景图片
 35:             QPalette bgPalette = this->palette();
 36:             bgPalette.setBrush(QPalette::Background,m_Pixmap);
 37:             T::setPalette(bgPalette);
 38:         }
 39:     }
 40:
 41: private:
 42:     QPixmap m_Pixmap;
 43: };

 

组件准备好后,我们就可以轻松的使用了

  1:    //创建一个异形窗体,支持move操作,基类为QWidget
  2:     WidgetRuleless< WidgetMove<QWidget> > wid;
  3:     wid.SetBackgroundImg("hudie.png");
  4:     wid.resize(640, 480);
  5:    //创建一个Button类,并且支持move操作
  6:     WidgetMove<ImgButton> btn(&wid);
  7:     btn.move(300,300);
  8:     wid.show();

 

简单几行代码,我们就可以得到以下效果:

效果图: 蝴蝶为异形窗体,小树为异形按钮,并且都支持move动作。

 

这里我们将经常用的小功能(主要是需要通过继承实现的),分解成若干小零件,在日常项目开发中,我们只需要进行简单的组合,就可以得到一个功能强大的控件。

后记:C++是一门异常强大的语言,模板的注入更为C++添加了无穷的潜力,十余年来,他的潜力不断被挖掘出来,但依旧有存在巨大的潜力等待我们去探索,去发现。

 

注:模板类中不支持Qt信号和槽的机制。

时间: 2024-10-11 21:20:10

C++链式继承的相关文章

C++ 链式继承下的虚函数列表

目录 1.虚函数列表的位置 2.虚函数列表的内容 3.链式继承中虚函数列表的内容 ? 注: 虚函数列表 又称为虚表, vtbl , 指向它的指针称为vptr, vs2019中称为__vfptr 操作系统: windows 10 专业版 64位 编译器: Visual Studio 2019 Community ? 1.虚函数列表的位置 结论 编译器一般会保证指向虚函数列表的指针存在于对象实例中最前面的位置 而虚函数列表中的内容, 就是多个函数指针 代码验证: 首先声明一个基类Base和一个派生类

对Javascript的原型,原型链和继承的个人理解

继承是OO语言中一个最为人津津乐道的概念,也是初接触Javascript的初学者难理解的概念=.=继承主要分为两种:一种是接口继承,另一种是实现继承.而在ECMAScript中只支持实现继承,所以我们今天来讨论讨论实现继承.实现继承就是继承实际的方法,主要依靠原型链来实现.讲到这里我们就需要讨论讨论什么是原型链. 1.什么是原型 要理解原型链我们首先要知道什么是原型.我们知道每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,这个对象包含所有实例共享的属性和方法.所以我个人

读书笔记之 - javascript 设计模式 - 接口、封装和链式调用

javascript 采用设计模式主要有下面的三方面原因: 可维护性:设计模式有助于降低模块之间的耦合程度.这使代码进行重构和换用不同的模块变得容易,也使程序员在大型项目中合作变得容易. 沟通:设计模式为处理不同类型的对象提供了一套通用的术语.程序员可以简洁的描述自己系统的工作方式. 性能:采用一些优化性能的模式,可以大幅度提高程序的执行效率,如享元模式和代理模式等 同时,滥用设计模式也会带来一些后果: 复杂性:代码变得复杂,新手难以理解 性能:多数设计模式会或多或少的降低代码的性能 实现容易,

JavaScript继承基础讲解,原型链、借用构造函数、混合模式、原型式继承、寄生式继承、寄生组合式继承

说好的讲解JavaScript继承,可是迟迟到现在讲解.废话不多说,直接进入正题. 既然你想了解继承,证明你对JavaScript面向对象已经有一定的了解,如还有什么不理解的可以参考<面向对象JS基础讲解,工厂模式.构造函数模式.原型模式.混合模式.动态原型模式>,接下来讲一般通过那些方法完成JavaScript的继承. 原型链 JavaScript中实现继承最简单的方式就是使用原型链,将子类型的原型指向父类型的实例即可,即“子类型.prototype = new 父类型();”,实现方法如下

JavaScript ES5类 原型 原型链 组合、原型、寄生式继承

ES5类 原型  原型链 继承 JavaScript中,原型是相对于构造函数(类)的叫法(或者说概念),原型链是相对于构造函数(类)的实例对象的叫法. 对于JavaScript对象,如果在对象自身上找不到该属性,那么就会向上沿着原型链继续查找该属性 创建一个ES5类 在ES5中,类是由函数名首字母大写的函数,通过关键字new创建的. 类的构造函数就是函数自身 一般情况下,ES5类的原型对象prototype是自身构造函数,该类的实例化对象的原型链对象__proto__也是该构造函数,这二者指向同

数据结构Java实现05----栈:顺序栈和链式堆栈

数据结构Java实现05----栈:顺序栈和链式堆栈 一.堆栈的基本概念: 堆栈(也简称作栈)是一种特殊的线性表,堆栈的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置进行插入和删除操作,而堆栈只允许在固定一端进行插入和删除操作. 先进后出:堆栈中允许进行插入和删除操作的一端称为栈顶,另一端称为栈底.堆栈的插入和删除操作通常称为进栈或入栈,堆栈的删除操作通常称为出栈或退栈. 备注:栈本身就是一个线性表,所以我们之前讨论过线性表的顺序存储和链式存储,对于栈来说,同样适

面向对象的链式调用

1. 对象的链式调用 function Chain(){ this.n=0;//属性不一定一开始的时候全部都要初始化 this.fn1=function(_obj){//this指向  new Chain()实例化的对象 alert(this.n++);//注意:alert(this.n++)与this.fn1中的this 不一定指向的对象是一样的 return this; } this.fn2=function(){//同上 alert(this.n++);//注意:alert(this.n+

第79讲:单例深入讲解及单例背后的链式表达式

今天我们来看一下scala中的单例及单例的链式表达式 让我们通过代码进行分析. object Scalaclass Java1class JVM{def method1:this.type=this}class JVM_Language extends JVM{def method2:this.type=this}object Singleton_type{def main(args:Array[String]){println(Scala.getClass)println(typeOf[Sca

javascript中类式继承和原型式继承的实现方法和区别

在所有面向对象的编程中,继承是一个重要的话题.一般说来,在设计类的时候,我们希望能减少重复性的代码,并且尽量弱化对象间的耦合(让一个类继承另一个类可能会导致二者产生强耦合).关于“解耦”是程序设计中另一个重要的话题,本篇重点来看看在javascript如何实现继承. 其它的面向对象程序设计语言都是通过关键字来解决继承的问题(比如extend或inherit等方式).但是javascript中并没有定义这种实现的机制,如果一个类需要继承另一个类,这个继承过程需要程序员自己通过编码来实现. 一.类式