zoukankan      html  css  js  c++  java
  • 从Qt谈到C++(二):继承时的含参基类与初始化列表

    提出疑问

    当我们新建一个Qt的图形界面的工程时,我们可以看看它自动生成的框架代码,比如我们的主窗口名称为MainWindow,我们来看看mainwindow.cpp文件:

    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
    }

    不同于一般的继承,这里的父类的括号里带有参数,我们通常都使用过不带参数,甚至不带括号的基类名称。这里的基类为什么带着参数呢?

    C++继承与构造函数

    不能继承父类构造函数

    C++中类的继承与Java中的不同,C++的派生类不能继承父类的构造函数和析构函数,只能继承父类的公有成员。所以这就会造成一种结果——我们无法使用基类的构造函数来对子类进行初始化!

    类比Java

    这里我假设大家都有Java的背景知识,没有也没关系,请听我讲。在Java中我们可以使用关键字super来直接调用父类的构造函数。比如我们定义两个类:Rectangle (矩形),Square (正方形)。从数学角度讲,正方形是一种特殊的矩形,所以我们的Square类继承自Rectangle类。在Java中像这样:

    class Rectangle{
        public Rectangle(int x,int y){
            length = x;
            width = y;
        }
        public void area(){
            System.out.println("The area is "+length*width);
        }
        private int length;
        private int width;
    }
    class Square extends Rectangle{//Java中使用extends关键字表示继承
    
        public Square(int x) {
            super(x, x);
        }
    }
    在派生类Square的构造函数中我们使用了,super这一关键字,它会默认调用基类的构造函数来初始化派生类。所以它相当于用一个整型x来初始化长方形的长和宽,所以我们得到的是一个正方形。可以验证一下,我们再使用一个类来验证这个Square是否可用,关键代码如下:

    	public static void main(String[] args) {
    		Square s = new Square(4);
    		s.area();
    	}
    打印结果就是  The area is 16

    C++实现方案

    先依样画葫芦,写个C++版Rectangle:

    class Rectangle
    {
    public:
        Rectangle(int x,int y);
        void area();
    private:
        int length;
        int width;
    };
    Rectangle::Rectangle(int x, int y)
    {
        length = x;
        width = y;
    }
    void Rectangle::area()
    {
        cout<<"The area is "<<length*width<<endl;
    }
    派生类的声明部分,我们也可以实现;

    class Square:public Rectangle
    {
    public:
        Square(int x);
    };

    这只是声明了Square的构造函数,但是我们该如何实现呢?我们的C++可是没有super这一关键字的,而且C++派生类不会继承基类的构造函数。

    有人说我可不可以这样:

    Square::Square(int x):Rectangle
    {
        Rectangle::Rectangle(x,x);
    }

    答案当然是 NO!!。为了解决这一矛盾,C++提供了继承含参基类的实现方法,实现构造函数的方法就是这样:

    Square::Square(int x):Rectangle(x,x)
    {
    
    }
    看懂没有,不要奇怪这个空函数体,我们所需要的初始化操作已经完成。在main函数中试试:

    int main()
    {
        Square s(4);
        s.area();
        return 0;
    }
    注意在声明部分就是和普通的继承声明是一样的,基类也不用加参。

    By the Way,讲一下一个类的构造过程。

    类的构造过程

    • 首先,它的基类(如果有)的构造函数被调用。
    • 然后,它的成员的构造函数被调用(如果有)。
    • 最后,它自己的构造函数被调用。

    当然了,这是题外话。

    初始化列表

    我们再回过头来看看,最初的那段Qt代码:

    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
    }
    现在,前面的 QMainWindow(parent) 这部分我们刚才讲完了。接下来看看后面的
    ui(new Ui::MainWindow)
    其实这就是构造函数的初始化列表。其实理解起来要容易的多。我们的类MainWindow有一个成员ui,它是MainWindow类型(这里ui的类型和它所处的类是相同的,这是特殊的情况,我们不用计较)。举个更一般的例子。

    还记得我们刚才的矩形么?我们可以使用这种方法来初始化它。

    Rectangle::Rectangle(int x, int y):length(x),width(y)
    {
    }
    效果等价于刚才的:

    Rectangle::Rectangle(int x, int y)
    {
        length = x;
        width = y;
    }
    明白了吧,但是其实两种有点不同,那就是系统先调用初始化列表来初始化,接着会调用构造函数体内部的代码来初始化,也就是说,后者会覆盖掉前者。
    Rectangle::Rectangle(int x, int y):length(x),width(y)
    {
        length = 2*x;
        width = 2*y;
    }
    
    然后main函数中:

    int main()
    {
        Rectangle r(3,4);
        r.area();
        return 0;
    }
    它的输出结果 是 The area is 48

    顺便一提,在一个对象初始化的时候会先调用其基类的构造函数,其成员对象的构造函数,最后是自己的构造函数。而析构的时候顺序恰好相反。






  • 相关阅读:
    css实现并列效果
    去除inline-block之间的间距
    鼠标点击<input>输入域后出现有颜色的边框
    消除a标签点击后产生的虚线框
    超过既定行数时,用省略号代替的方法
    常用按钮样式
    常用颜色
    通过Gulp流方式处理流程
    IntelliJ IDEA 10 配置 Tomcat7
    chrome浏览器调试线上文件映射本地文件
  • 原文地址:https://www.cnblogs.com/unclejelly/p/4082079.html
Copyright © 2011-2022 走看看