从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

从Qt谈到C++(二):继承时的含参基类与初始化列表,布布扣,bubuko.com

时间: 2024-12-21 08:07:29

从Qt谈到C++(二):继承时的含参基类与初始化列表的相关文章

多继承时,多个基类中存在型别相同的虚函数,该怎么做?

#include "stdafx.h" #include<iostream> using namespace std; class A{ public: virtual void show(){ cout<<"in classA"<<endl; } virtual ~A(){} }; class B{ public: virtual void show(){ cout<<"in classB"<

C#继承机制 访问与隐藏基类成员

(1) 访问基类成员 通过base 关键字访问基类的成员:   调用基类上已被其他方法重写的方法.  指定创建派生类实例时应调用的基类构造函数.  基类访问只能在构造函数.实例方法或实例属性访问器中进行. 从静态方法中使用 base 关键字是错误的. 示例:下面程序中基类 Person 和派生类 Employee 都有一个名为 Getinfo 的方法.通过使用 base 关键字,可以从派生类中调用基类上的 Getinfo 方法. using System ;public class Person

第十二周程序阅读5:基类和派生类的转换

问题及代码: #include <iostream> using namespace std; class A { protected: int a,b; public: A(int aa, int bb):a(aa), b(bb) {} void printA() { cout<<"a: "<<a<<"\tb: "<<b<<endl; } }; class B: public A { int

Part7 继承与派生 7.3基类与派生类类型转换

公有派生类对象可以被当作基类的对象使用,反之则不可. 派生类的对象可以隐含转换为基类对象: 派生类的对象可以初始化基类的引用: 派生类的指针可以隐含转换为基类的指针. 通过基类对象名.指针只能使用从基类继承的成员. //7-3类型转换规则举例 #include<iostream> using namespace std; class Base1{ public: void display() const { cout << "Base1::display()"

C++ Primer学习笔记32_面向对象编程(3)--继承(三):多重继承、虚继承与虚基类

C++ Primer学习笔记32_面向对象编程(3)--继承(三):多重继承.虚继承与虚基类 一.多重继承 在C++语言中,一个派生类可以从一个基类派生,称为单继承:也可以从多个基类派生,称为多继承. 多重继承--一个派生类可以有多个基类 class <派生类名> : <继承方式1> <基类名1>,<继承方式2> <基类名2>,... { <派生类新定义成员> }; 可见,多继承与单继承的区别从定义格式上看,主要是多继承的基类多于一个

C++ 虚基类 派生与继承

在学习设计模式时我就有一个疑问,关联和继承除了用法上的区别,好像在内存上并没有什么区别,继承也是父类作为了子类的元素(内存上),关联也是这样.而且关联好像更占内存一些.这就是设计模式里问题了“依赖倒转原则”. 继承分为public继承,protect继承,private继承 public:父类中的public,protected成员到了派生类中属性不变. protected:父类中的public,protected成员到了派生类中,都变为protected成员. private:父类中的publ

类型转换,类与类之间的转换,继承关系,继承与静态变量,子类父类重名,多继承,虚基类

 常量的基本类型转换,例如:int num(10.8),这种方式是隐式转换. 通过函数的构造函数实现转换. 类类转换函数,当构造函数不能将类型转换成基本类型时.所以就有了类类转换函数,通过这种方式. 案例: #include <iostream> class fushu { public: //通过加explicit的这种方式避免隐式转换,避免引发歧义 explicit fushu(int num) { x = num; y = num; } void print() { std::cou

面向对象程序设计——抽象基类,访问控制与继承,继承中的类作用域,拷贝函数与拷贝控制

一.抽象基类 1)纯虚函数 和普通的虚函数不同,一个纯虚函数无须定义.我们通过在函数体的位置(即在声明语句的分号之前)书写=0就可以将一个虚函数说明为纯虚函数.其中,=0只能出现在类内部的虚函数声明语句处. 值得注意的是,我们也可以为纯虚函数提供定义,不过函数体必须定义在类的外部.也就是说,我们不能在类的内部为一个=0的函数提供函数体. 2)含有纯虚函数的类是抽象基类 含有(或者未经覆盖直接继承)纯虚函数的类是抽象基类.抽象基类负责定义接口,而后续的其他类可以覆盖该接口.我们不能直接创建一个抽象

(C++)浅谈多态基类析构函数声明为虚函数

主要内容: 1.C++类继承中的构造函数和析构函数 2.C++多态性中的静态绑定和动态绑定 3.C++多态性中析构函数声明为虚函数 1.C++类继承中的构造函数和析构函数 在C++的类继承中, 建立对象时,首先调用基类的构造函数,然后在调用下一个派生类的构造函数,依次类推: 析构对象时,其顺序正好与构造相反: 具体参考文章:http://www.cnblogs.com/AndyJee/p/4575385.html 2.C++多态性中的静态绑定和动态绑定 对象的静态类型:对象在声明是采用的类型,在