C++类继承构造函数的语法 & initialization list初始化

来源:互联网 发布:秋诗词散文 知乎 编辑:程序博客网 时间:2024/04/30 15:02

C++类继承构造函数的语法 

一,父类没有写出构造函数,

子类的构造函数可以写成“任何”形式而不用顾及父类的构造函数。
例如:父类为Father,子类为Son ,父类中没有写出任何构造函数,则子类中可以不写构造函数,或者构造函数可以写成以下几种:
Son(){};
Son():Father(){};
Son(int a, int b){};
Son(int a, int b):Father(){};

这几种形式都可以。即完全忽略父类的构造函数。


二,父类只写出了一个无参构造函数。
则子类可以不写构造函数,或者写成以下几种。

Son(){};

Son():Father(){}

Son(int a, int b){};
Son(int a, int b):Father(){};

即当父类只写出一个无参构造函数或者没有写构造函数效果是一样的。


三,父类只写出了一个有参构造函数
此时,子类应该“标准”地写出构造函数,例如
Son(int a, int b){};

Son(int a, int b):Father(a,b){ };


或者:
Son(int a, int b){};

Son(int a, int b ,int c):Father(a,b){};

意义就是把子类构造函数传递进来的参数,再传递给父类的构造函数


四,父类有多个有参构造函数,
此时,子类最少实现一个父类的构造函数即可。
五,父类有多个有参函数和一个无参构造函数。
此时,子类只需实现一个父类的构造函数即可,不管子类实现的是有参构造函数还是无参构造函数。
这时候应该注意,即使子类写成Son(){};也是正确的,说明父类的无参构造函数可以忽略。甚至写成Son(int a, int b){};也是正确的。
总结以上几条可以归纳出C++子类继承父类时构造函数的写法的规律为:
1,当父类有显式写出的构造函数时,子类最低限度的实现父类中的一个。
2,当父类没有显式写出构造函数时,子类可以不写构造函数或者“自由”书写构造函数。

更为普遍的规律总结如下:子类的构造函数以最少满足父类的一个构造函数为准。


Initialization List的使用

有一种用法,是在写构造函数的实现时,紧接着就给类的成员变量初始化,而不是一定要在构造函数括号内部初始化类的成员变量。

看如下例子的红色部分,作用是给member_var这个成员变量初始化为0


例1:


class A
{
  public:
    int member_var; //成员变量
    A();            //构造函数
}
A::A():member_var(0)
{
}
 
他们觉得这个构造函数的定义应该只能这样写:
A::A()
{
   member_var=0;
}


为什么要用第initialization list的方法初始化呢?
 
其实两种方法都可。但是有些情况下,只能用第一种,而且通常情况下用第一种也会效率高些。
 
其实,第一种方法是真正的初始化(initialization),而在构造函数内实现的“=”操作其实是赋值(assign)。这两种方法的一切区别从这儿开始。区别大概如下:
 
我们知道普通变量编译器都会默认的替你初始化。他们既能初始化,也能被赋值的,而常量(const)按照其意思只能被初始化,不能赋值。否则与变量就无区别了。所以常量成员(const member)只能用成员初始化列表来完成他们的“初始化”,而不能在构造函数内为他们“赋值”。



再来看一个例子,注意两个问题,(1)用initialization list初始化多个成员变量 (2)初始化的顺序

例2

#include <iostream>
using namespace std;
 
class MemberInitializationList
{
private:
         int i;
         int j;
public:
         MemberInitializationList(int val) : j(val), i(j)        

         // j(val), i(j)就是所谓的成员初始化列表initialization list,含义就是用传入的val初始化成员变量j,用成员变量j初始化成员变量i,
         {       

          }
         inline void printInfo()
         {
                   cout << "i = " << i << ", j = " << j << endl;
         }
};
 
int main(void)
{
         MemberInitializationList MIL(10);
         MIL.printInfo();
        
         return 0;
}

运行结果:

j如愿以偿被初始化为10,但是i的值是一个未被初始化的奇怪数字。


关于顺序问题:
答案是有些细微的地方需要注意:成员初始化列表的初始化顺序是有类中的成员声明次序决定的,而不是由initialization list中的排列次序决定的。也就是说,即使是你先写了j(val), i(j), 因为你先声明的i,所以先用j去初始化了i。在本例中,先初始化i然后再初始化j。initialization list中的i(j),表明将j的值赋给i,而此时j还没有被初始化,其值不确定,所以i的值也就不能确定,这就是运行结果中为什么i的值比较奇怪的原因了。

如果把本例中的构造函数改成:
         MemberInitializationList(int val) : i(val), j(i)
         {


         }
再运行的结果就正确了


二者在Qt中的结合,语法解析


Qt中默认构造出的第一个widget或window都会有如下经典的程序语句


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


分析一下,首先

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)

这个是第一层意思,代表将parent传递给了父类。

MainWindow::MainWindow(QWidget *parent) : ui(new Ui::MainWindow)

这个是第二层意思,表示用一个new的Ui命名空间 中的MainWindow类对象初始化了成员变量ui


两个合在一起写,冒号后的内容要用逗号并列,就像初始化俩变量一样,用逗号并列俩变量。

1 0