虚函数最好不要内联

本文探讨了虚拟函数内联可能导致的警告及其原因。当类包含虚函数时,会为每个对象分配一个指向虚函数表的指针,增加额外的内存开销。如果类在头文件中定义且所有虚函数都在类内,每个包含该头文件的编译单元都会有自己的虚函数表副本,导致编译后文件增大和链接时间增加。因此,至少需要一个行外定义的虚函数来避免这种情况。

虚函数最好不要内联

现象

在这里插入图片描述

定义在内联函数时会有如下警告:

警告:'Sp_counter’没有行外的虚方法定义;它的虚函数表将在每个翻译单元中发出,意思就是程序编译时候会给每个有虚函数的类生成一个虚函数表然后放在.o里,程序运行后拷贝到内存里。

放在类外之后就正常了
在这里插入图片描述

原因

类的实例化就是给每一个实例在内存中分配一块地址。空类的大小是0,实例化时编译器会给一个字节。

class A {
  public:
    virtual ~A();
};
A::~A() {}

class AA {
  public:
    ~AA() {}
};
int main() {
    std::cout << sizeof(A) << ", " << sizeof(AA) << std::endl;
    // 结果 8, 1 (gcc 64)
}

如果类中有虚函数,多8个字节(gcc 64)。任何有虚函数的类,都会多一个地址的大小,存放虚函数表的位置。为了高效,这个放地址的位置在这个类的存储空间最前面。

每一个有虚函数的类都有一个虚函数表,这个类的所有对象都放着这个表的指针。程序编译时候会给每个有虚函数的类生成一个虚函数表放在.o里,程序运行后拷贝到内存里。**

如果一个类是在头文件中定义的,并且有虚函数表(它有虚方法,或者它从有虚方法的类派生),那么它必须在类中至少有一个行外的虚方法。如果不这样做,编译器就会把vtable和RTTI复制到每一个包含了#include头文件的.o文件中,这会使.o文件的大小膨胀,并增加链接时间

  1. 参考文章1
  2. 参考文章2
<think>好的,我现在需要回答用户的问题:“内联成员函数可以作为虚函数吗?”首先,我得理清楚内联函数和虚函数的基本概念,以及它们在C++中的相互作用。 首先,内联函数。内联函数通常是通过在函数定义前加上inline关键字来建议编译器将函数体直接插入到调用处,以减少函数调用的开销。不过,编译器有权决定是否真正内联,尤其是当函数体较复杂时,可能会忽略inline的请求。 然后是虚函数虚函数用于实现多态性,允许在派生类中重写基类的函数。当通过基类指针或引用调用虚函数时,会根据对象的实际类型调用相应的函数实现。这通常涉及到虚函数表(vtable)的机制,每个含有虚函数的类都有一个vtable,其中存放着指向各个虚函数实现的指针。 现在问题来了:内联成员函数能否作为虚函数?这里可能存在一些矛盾点。因为内联函数在编译时展开,而虚函数的调用是在运行时动态决定的。但C++的语法是否允许同时使用inline和virtual关键字呢? 我记得在C++中,函数的声明和定义可以分开。通常,虚函数在基类中声明为virtual,而具体的实现在派生类中。内联函数通常是在头文件中定义,以便编译器在调用处展开。如果将一个虚函数同时声明为内联,会发生什么? 首先,语法上是否允许?例如: ```cpp class Base { public: virtual inline void func() { /* 实现 */ } }; ``` 或者,在类定义内部直接定义虚函数,这时函数默认是内联的: ```cpp class Base { public: virtual void func() { /* 实现 */ } // 这里func()是虚函数,同时也是内联的? }; ``` 这种情况下,虽然函数在类内定义,自动成为内联函数,但同时它也是虚函数。这时候,编译器如何处理? 可能的情况是,inline在这里只是一个建议,编译器可以选择是否内联。而虚函数的关键在于其多态性,通过vtable调用。因此,即使函数被声明为内联,只要它是虚函数,调用时仍会通过vtable进行动态绑定,除非在编译时可以确定具体调用的对象类型,这时编译器可能进行优化,直接调用具体函数,从而可能内联。 另一个角度是,当虚函数在基类中有内联的定义,而在派生类中被重写,此时基类的内联版本可能仅在对象静态类型为基类时被内联,而通过基类指针或引用调用时,仍会动态分派到派生类的实现,此时无法内联派生类的函数,除非编译器能确定具体类型。 但问题是,C++标准是否允许将虚函数声明为内联?根据我的理解,语法上是允许的,因为inline和virtual是两个不同的关键字,可以同时使用。但实际效果如何? 比如,在基类中内联定义的虚函数,是否会被编译器处理为内联?可能的情况是,当调用该函数时,如果对象的静态类型已知(如直接调用基类对象的方法),则可能内联;但如果通过指针或引用调用,则仍走虚表,无法内联。因此,从语法上来说是可以的,但实际是否内联取决于上下文和编译器优化。 另外,当虚函数在类内定义时(隐式内联),此时它作为虚函数是否有效?答案是肯定的。例如: ```cpp class Base { public: virtual void func() { cout << "Base"; } }; class Derived : public Base { public: void func() override { cout << "Derived"; } }; ``` 这里,Base::func()是虚函数,并且隐式内联。当通过Base指针调用func()时,会正确调用Derived的版本,说明虚函数机制正常工作。因此,内联虚函数在语法和功能上都是允许的,但内联的实际效果可能受限于调用方式。 总结:内联成员函数可以作为虚函数。C++允许将虚函数声明为内联,但内联是否生效取决于编译器及调用上下文。当通过基类指针或引用调用虚函数时,通常无法内联,因为需要在运行时确定具体函数;而直接调用对象实例的虚函数时,可能被内联处理。因此,技术上可行,但实际效果因情况而异。</think>内联成员函数可以作为虚函数。以下是详细分析: **1. 语法层面** - C++允许将虚函数声明为`inline`,二者在语法上不冲突。 - 示例: ```cpp class Base { public: virtual inline void func() { /* 基类实现 */ } // 显式声明为内联虚函数 }; ``` **2. 实际效果** - **内联的可行性**: - 当通过**对象实例直接调用**虚函数时(如`obj.func()`),若编译器能确定具体类型,可能内联该函数。 - 当通过**基类指针/引用调用**虚函数时(如`ptr->func()`),由于需动态绑定,通常通过虚函数表(vtable)查找,无法内联。 - **隐式内联虚函数**: ```cpp class Base { public: virtual void func() { /* 实现 */ } // 类内定义,隐式内联 }; ``` 此时`func()`既是虚函数又是内联函数,但虚函数的动态调用特性会优先于内联优化。 **3. 使用建议** - **适用场景**: - 虚函数体简单且频繁调用,且多数调用可静态确定类型(如直接对象调用)。 - 例如:基类提供默认实现,派生类可能重写,但部分场景仍直接使用基类对象。 - **注意事项**: - 内联虚函数**不会影响多态性**,动态调用仍通过vtable。 - 过度使用可能导致代码膨胀(多个内联副本),需权衡性能收益。 **总结**: 内联成员函数可以声明为虚函数,语法正确且功能正常,但内联优化仅在特定调用场景下生效。设计时应根据实际调用模式决定是否结合使用。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-西门吹雪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值