受助于许许多多的博客文章,第一次回馈!写得不好,请多见谅。
关于C++初始化列表的一种特殊用法
Canvas::Canvas(Gwen::Skin::Base* pSkin) : BaseClass(NULL), m_bAnyDelete(false), m_fScale(-1)
{
SetBounds(0, 0, 10000, 10000);
SetSkin(pSkin);
SetScale(1.0f);
SetBackgroundColor(Color(255, 255, 255, 255));
SetDrawBackground(false);
}
这是今天在看bullet3源码时,发现的一点不解之处。
BaseClass(NULL)
正常来说,在初始化列表这个位置,像m_bAnyDelete(false), m_fScale(-1)
都是初始化一个变量的作用。
但是,这里的BaseClass却是一个类。
不解。
为了方便,我复现了代码:
#include <iostream>
class A
{
public:
A(int* n)
{
printf("调用 A 的构造函数\n");
}
};
class B : public A
{
int k;
public:
B() : A(NULL), k(1)
{
printf("调用 B 的构造函数\n");
}
};
int main()
{
B b;
return 0;
}
在与舍友的讨论之下,他的一句话启发了我:“你的A是基类 不是成员。。”
我恍然大悟:
基类!
在类的构造函数被调用的时候,首先是跳到基类的构造函数上去的。
那么,在构造函数这里的初始化列表,出现了基类,是否意味着什么呢?
噎死!(yes)
首先我删除了A(NULL)这部分,然后编译失败:不存在默认构造函数。
!!!
那就是说,如果加了A(NULL)这部分,那么在类调用构造函数时候,它调用的基类的构造函数,将不是默认构造函数,而且有参数版本!!!
因为一般来说,都是不加A(NULL)的,自然调用的基类的构造函数,是默认版本。
!!!!!!!!!!!
那么A(NULL)这个部分的作用,就不是“初始化”了,而且“指定基类的构造函数”了。
21:44:27
那么,原本没写A(NULL),是调用 A() 这个构造函数的,
如果写了 A(NULL),那么就调用 A(int* n) 这个构造函数。
21:44:48
区别就是:调用不同的 基类的 构造函数。
来执行:不同的 对基类成员的 初始化操作。
比如默认构造函数是 a=1
换成另一个构造函数,就是 a=2
就是这个区别。
在补了默认构造函数之后,不加A(NULL)也不报错了。
加了如下代码:
则结果显示上图的printf语句。(红框里的“—”只是为了方便区分)
。
。
。
加了A(NULL)之后,则调用的是基类的A(int* n)这个版本的构造函数。
。
。
。
。
21:50:39
“执行基类的非默认构造函数”
这只有初始化列表,才能这样子做。
没有其他办法。
21:55:04
如果以后公司让你面试新人,可以出这个题了 哈哈哈哈哈哈哈哈哈哈
问他们:在构造函数的初始化列表中,“对类型进行初始化”是毛意思。
答:这不是对类型初始化,而且对基类调用非默认构造函数,因为如果不写,那就调用默认构造函数。
. 21:57:09
其实没啥特别的,你这里的 B的构造函数 就是调用基类的构造函数了而已,参数为NULL
. 21:57:18
B的初始化列表 必须要初始化A
. 21:57:27
除非你的A里面有 不带参数 的构造函数
. 21:57:40
就是 说 无论如何 你也要初始化A才能完事
21:58:03
特别就在这里:“执行基类的非默认构造函数”
“非默认构造函数”
21:58:41
这个写法,很容易让人误以为是赋值操作啊
21:59:07
毕竟位置是“初始化列表”,听名字就是用来初始化的
21:59:29
鬼知道还能“调用基类的构造函数”
后期补充:
C++派生类中调用基类成员函数和构造函数
https://www.jianshu.com/p/6bc25c22a1df
假设这样一个情况,我们在派生类的构造函数中,希望调用基类的构造函数,但基类没有和派生类构造函数参数相匹配的构造函数。例如下面:
class Base {
public:
Base(int i) { // 基类没有默认构造函数,当前构造函数仅接受一个参数
cout << "调用基类构造函数" << endl;
};
};
class Derived : public Base {
public:
Derived(int i, int j) { // 派生类构造函数接受两个参数,语法错误,no default constructor exists for class Base
cout << "调用派生类构造函数" << endl;
};
};
默认情况下,派生类会调用基类相同参数的构造函数,但这里没有相同参数的构造函数,便会有语法错误,即时我们在派生类的构造函数里调用Base::Base()也不行。解决方法是,在派生类构造函数的初始化列表里调用基类的构造函数,如下:
class Base {
public:
Base(int i) { // 基类没有默认构造函数,当前构造函数仅接受一个参数
cout << "调用基类构造函数" << endl;
};
};
class Derived : public Base {
public:
Derived(int i, int j): Base(i) { // 派生类构造函数接受两个参数,语法错误,no default constructor exists for class Base
cout << "调用派生类构造函数" << endl;
};
};
int main()
{
Derived d(1, 2);
return 0;
}