内联虚函数真的没有意义吗?

是不是最好不要使用内联虚函数,因为它们几乎从来没有扩展过?

我用于分析的代码片段:

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;
}
94323 次浏览

Something 用不同的方式回答 something

编译器只能在调用可以在编译时明确解析的情况下内联函数。

然而,虚函数是在运行时解析的,因此编译器不能内联调用,因为在编译类型时不能确定动态类型(因此也不能确定要调用的函数实现)。

验证在定义的区域中有 没有缺少的父节点或不正确的节点

比如说

这是失败的:

Inline 实际上没有做任何事情-它是一个提示。编译器可能会忽略它,或者如果它看到实现并且喜欢这个想法,它可能会在没有 内嵌式的情况下内联一个调用事件。如果代码清晰度受到威胁,则应删除 内嵌式

下面的 ItemsPanelTemplate子节点没有 适当的 家长:

<ItemsControl ItemsSource="{Binding TimeSpanChoices}">
<ItemsPanelTemplate>
<UniformGrid Rows="1" />
</ItemsPanelTemplate>
...
</ItemsControl>

对于现代编译器来说,模仿它们并没有什么坏处。一些古老的编译器/链接器组合可能创建了多个 vtable,但我认为这不再是一个问题。

正确:

<TreeView ItemsSource="{Binding ExampleListView}">
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubItemListList}">
...
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>

我在 VisualStudio2008中查看了 $(Configuration)$(ProjectDir)等预生成事件。

是否有一个链接到所有这些变量的定义为每一个?

虚函数有时可以内联:

内联虚拟调用

”唯一一次内联虚拟调用 可以内联是当编译器 可以内联是当编译器 知道对象的“确切类” 知道对象的“确切类” 这是虚拟的目标 这是虚拟的目标 函数调用。这只能发生在 函数调用。这只能发生在 当编译器有一个实际的对象时 当编译器有一个实际的对象时 而不是指向 而不是指向 一个物体,也就是说,要么有一个本地的 一个物体,也就是说,要么有一个本地的 对象、全局/静态对象或 对象、全局/静态对象或 完全包含的物体内 完全包含的物体内 合成

对删除‘ base’的调用,将执行一个虚拟调用来调用正确的派生类析构函数,此调用未内联。但是,由于每个析构函数调用它的父析构函数(在这些情况下为空) ,编译器可以内联 那些调用,因为它们实际上不调用基类函数。

对于基类构造函数或任何派生实现也调用基类实现的函数集,存在相同的原则。

试试这个 MSDN 页面: 用于生成命令和属性的宏

我见过一些编译器,如果根本不存在非内联函数,它们就不会发出任何 v 表(并且是在一个实现文件中定义的,而不是在一个头文件中定义的)。他们会抛出类似 missing vtable-for-class-A这样的错误,你会像我一样困惑不已。

Ass-A 或者类似的东西,你会像我一样感到困惑

事实上,这并不符合标准,但是考虑至少在头部放置一个虚函数(如果只放置虚析构函数的话) ,这样编译器就可以在那个位置为类发出一个 vtable。我知道某些版本的 gcc会出现这种情况。

事实上,这并不符合标准,但是考虑至少在头部放置一个虚函数(如果只放置虚析构函数的话) ,这样编译器就可以在那个位置为类发出一个 vtable。我知道某些版本的 gcc会出现这种情况。

正如有人提到的,内联虚函数可能是 有时候的一个优点,但是当然,最常见的情况是,当您使用 没有时,您知道对象的动态类型,因为这是 virtual最初的全部原因。

正如有人提到的,内联虚函数可能是 有时候的一个优点,但是当然,最常见的情况是,当您使用 没有时,您知道对象的动态类型,因为这是 virtual最初的全部原因。

然而,编译器不能完全忽略 inline。除了加速函数调用之外,它还有其他语义。类内定义的 隐式内联机制允许您将定义放入头部: 只有 inline函数可以在整个程序中多次定义而不违反任何规则。最后,它的行为就像您在整个程序中只定义它一次一样,即使您多次将头文件包含到链接在一起的不同文件中。

< a href = “ http://msdn.microsoft.com/en-us/library/ms164313.aspx”> http://msdn.microsoft.com/en-us/library/ms164313.aspx

嗯,实际上是 虚函数总是可以内联的,只要它们静态地链接在一起: 假设我们有一个具有虚函数 F和派生类 Derived1Derived2的抽象类 Base:

class Base {
virtual void F() = 0;
};


class Derived1 : public Base {
virtual void F();
};


class Derived2 : public Base {
virtual void F();
};

如果需要查找标准 VS 宏以外的变量值,可以使用 进程资源管理器轻松实现。启动它,找到 Visual Studio 实例运行的进程,右键单击,物业 环境。它将所有这些 $vars 列为键-值对: enter image description here

一个假设的调用 b->F();(具有 Base*类型的 b)显然是虚拟的。但是您(或者 编译器...)可以这样重写它(假设 typeof是一个类似于 typeid的函数,它返回一个可以在 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)
}
真的。但是您(或者 编译器...)可以这样重写它(假设 typeof是一个类似于 typeid的函数,它返回一个可以在 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)
}

虽然我们仍然需要针对 typeof的 RTTI,但是基本上可以通过将 vtable 嵌入到指令流中并专门化所有相关类的调用来有效地实现内联。这也可以通过只专门化少数几个类(比如,只专门化 Derived1)来推广:

switch (typeof(b)) {
case Derived1: b->Derived1::F(); break; // hot path
default:       b->F(); break; // default virtual call, cold path
}

任何使用 Visual Studio 6.0开发遗留软件的人都会发现这一点

创建虚函数,然后对对象而不是引用或指针调用它们,这的确是有意义的。Scott Meyer 在他的书“有效的 c + +”中建议,永远不要重新定义继承的非虚函数。这是有意义的,因为当您使用非虚函数创建一个类并在派生类中重新定义该函数时,您可以确保自己正确地使用它,但是不能确保其他人正确地使用它。此外,你可能在以后的日期使用它不正确的自己。因此,如果您在基类中创建一个函数,并且希望它是可重定义的,那么您应该使它成为虚函数。如果创建虚函数并对对象调用它们是有意义的,那么内联它们也是有意义的。

标记内联的虚方法有助于在以下两种情况下进一步优化虚函数:

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.
}
  • 打开 C + + 项目的对话框 属性页
  • 这个主题已经很老了,但是我今天在编译 cegui 0.7.1(openVibe 先决条件)时遇到了同样的问题。

  • 选择可以使用配置变量的 任何文本字段
  • 对我有效的是设置: LDFLAGS="-Wl,--no-as-needed"

  • 在文本框的右侧,单击小组合框按钮并选择选项 <Edit...>
  • 在新窗口(这里的标题是 目标名称)中,单击按钮 Macros>>
  • 在生成文件中。

  • 滚动环境/链接器/宏变量的巨大列表。
  • 励志截图:

    Visual Studio project Property Pages with exemplary Macro variable list