虚函数 inline

关于虚函数的说法,哪些是正确的( )

A. 构造函数中调用该类的虚函数,动态绑定机制会生效

B. 静态函数不可以是虚函数

C. 虚函数可以声明为inline

D. 构造函数和析构函数都可以声明为虚函数

函数绑定: 找到函数名对应的地址,然后将函数调用处用该地址替换,这称为函数绑定。

  • 静态绑定: 在编译期间(包括链接期间)就能找到函数名对应的地址,完成函数的绑定,程序运行后直接使用这个地址即可。
  • 动态绑定: 编译期间想尽所有办法都不能确定使用哪个函数,必须要等到程序运行后根据具体的环境或者用户操作才能决定。

构造函数中调用虚函数问题:

在构造或析构函数中调用虚函数会执行与之所属类型相对应的虚函数版本。

所以当构造函数中有虚函数时,编译期间就可以确定此虚函数为本类中的函数,不需要再动态绑定。


虚函数内联问题:

虚函数可以是内联函数,但是当虚函数表现多态性的时候不能内联
理由如下:内联是在发生在编译期间,编译器会自主选择内联,而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因此虚函数表现为多态性时(运行期)不可以内联。 inline virtual唯一可以内联的时候是:编译器知道所调用的对象是哪个类,这只有在编译器具有实际对象而不是对象的指针或引用时才会发生,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Base{
public:
inline virtual void who(){
cout << "I am Base\n";
}
virtual ~Base() {}
};
class Derived : public Base{
public:
inline void who() { // 不写inline时隐式内联
cout << "I am Derived\n";
}
};

int main()
{
// 此处的虚函数 who(),是通过类(Base)的具体对象(b)来调用的,
// 编译期间就能确定了,所以它可以是内联的,但最终是否内联取决于编译器。
Base b;
b.who();

// 此处的虚函数是通过指针调用的,呈现多态性,需要在运行时期间才能确定,
// 所以不能为内联。
Base *ptr = new Derived();
ptr->who();
}

静态函数不能是虚函数!

虚函数是与类对象捆绑的。而类的普通成员函数(包括虚函数)在编译时加入this指针,通过这种方式可以与对象捆绑,而静态函数编译时不加this,因为静态函数是给所有类对象公用的,所以没有在编译时加this,所以无法与对象捆绑,而虚函数就是靠着与对象捆绑加上虚函数列表才实现了动态绑定。所以没有this指针虚函数无从谈起。