aJ.*_*aJ. 167 c++ virtual-functions inline
当我收到代码评论评论说虚拟功能不需要内联时,我收到了这个问题.
我认为在直接在对象上调用函数的场景中,内联虚函数可以派上用场.但是我想到的反驳论点是 - 为什么要想定义虚拟然后使用对象来调用方法呢?
最好不要使用内联虚拟功能,因为它们几乎从未扩展过吗?
我用于分析的代码片段:
class Temp
{
public:
virtual ~Temp()
{
}
virtual void myVirtualFunction() const
{
cout<<"Temp::myVirtualFunction"<<endl;
}
};
class TempDerived : public Temp
{
public:
void myVirtualFunction() const
{
cout<<"TempDerived::myVirtualFunction"<<endl;
}
};
int main(void)
{
TempDerived aDerivedObj;
//Compiler thinks it's safe to expand the virtual functions
aDerivedObj.myVirtualFunction();
//type of object Temp points to is always known;
//does compiler still expand virtual functions?
//I doubt compiler would be this much intelligent!
Temp* pTemp = &aDerivedObj;
pTemp->myVirtualFunction();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
ya2*_*a23 144
有时可以内联虚函数.优秀的C++ faq的摘录:
"内联虚拟调用唯一可以内联的是编译器知道作为虚函数调用目标的对象的"确切类".这只有在编译器具有实际对象而不是指针时才会发生.引用一个对象.即,使用本地对象,全局/静态对象或复合内部的完全包含的对象."
MSa*_*ers 70
C++ 11已添加final
.这改变了接受的答案:不再需要知道对象的确切类,只需知道对象至少具有声明final的函数的类类型即可:
class A {
virtual void foo();
};
class B : public A {
inline virtual void foo() final { }
};
class C : public B
{
};
void bar(B const& b) {
A const& a = b; // Allowed, every B is an A.
a.foo(); // Call to B::foo() can be inlined, even if b is actually a class C.
}
Run Code Online (Sandbox Code Playgroud)
Ric*_*den 36
有一类虚拟功能,让它们内联仍然有意义.考虑以下情况:
class Base {
public:
inline virtual ~Base () { }
};
class Derived1 : public Base {
inline virtual ~Derived1 () { } // Implicitly calls Base::~Base ();
};
class Derived2 : public Derived1 {
inline virtual ~Derived2 () { } // Implicitly calls Derived1::~Derived1 ();
};
void foo (Base * base) {
delete base; // Virtual call
}
Run Code Online (Sandbox Code Playgroud)
删除'base'的调用将执行虚拟调用以调用正确的派生类析构函数,此调用不会内联.但是因为每个析构函数都调用它的父析构函数(在这些情况下为空),编译器可以内联这些调用,因为它们不会虚拟地调用基类函数.
基类构造函数或任何函数集也存在相同的原则,其中派生实现也调用基类实现.
Joh*_*itb 14
如果根本没有非内联函数存在(并且在一个实现文件中定义而不是在头部中定义),我已经看到了不发出任何v表的编译器.他们会抛出类似missing vtable-for-class-A
或类似的错误,你会像我一样被困惑.
实际上,这与标准不符,但它确实发生了考虑将至少一个虚函数放在标题中(如果只是虚拟析构函数),这样编译器就可以在该位置为类发出vtable.我知道它发生在一些版本的gcc
.
正如有人提到的,内联虚拟函数有时可能是一种好处,但当然,当您不知道对象的动态类型时,通常会使用它,因为这是首先的全部原因virtual
.
然而,编译器不能完全忽略inline
.除了加速函数调用之外,它还有其他语义.类内定义的隐式内联是允许您将定义放入标题的机制:只有inline
函数可以在整个程序中多次定义,而不会违反任何规则.最后,它的行为与您在整个程序中只定义一次一样,即使您将标题多次包含在链接在一起的不同文件中.
CAF*_*FxX 10
好吧,实际上虚拟函数总是可以内联,只要它们静态链接在一起:假设我们有一个Base
带有虚函数F
和派生类的抽象类,Derived1
并且Derived2
:
class Base {
virtual void F() = 0;
};
class Derived1 : public Base {
virtual void F();
};
class Derived2 : public Base {
virtual void F();
};
Run Code Online (Sandbox Code Playgroud)
一个hypotetical调用b->F();
(b
类型Base*
)显然是虚拟的.但是你(或编译器 ......)可以像这样重写它(假设typeof
是一个类似typeid
函数,返回一个可以在a中使用的值switch
)
switch (typeof(b)) {
case Derived1: b->Derived1::F(); break; // static, inlineable call
case Derived2: b->Derived2::F(); break; // static, inlineable call
case Base: assert(!"pure virtual function call!");
default: b->F(); break; // virtual call (dyn-loaded code)
}
Run Code Online (Sandbox Code Playgroud)
虽然我们仍然需要RTTI,但是typeof
通过将vtable嵌入到指令流中并专门调用所有涉及的类,可以有效地内联调用.这也可以通过仅专门化几个类(例如,仅仅Derived1
)来概括:
switch (typeof(b)) {
case Derived1: b->Derived1::F(); break; // hot path
default: b->F(); break; // default virtual call, cold path
}
Run Code Online (Sandbox Code Playgroud)