C++ 中哪些函数不能是虚函数,理由?

西昆仑 发布于 2012/03/12 16:17
阅读 4K+
收藏 1

在C++中有些函数能够为虚函数,有些不能,比如构造函数不能为虚函数,而析构函数可以。

那么具体的理由呢。

 

还有哪些其他类型的函数不能为虚函数呢。

加载中
1
从来不登录
从来不登录

构造函数(默认,拷贝)都不能是虚函数

如果这两种构造函数可以是虚函数,那么虚函数都要通过查询虚表来调用,要执行构造函数就要去查询虚表,可是这个时候类都还没构造完成呢,哪来的虚表可以查询呢

西昆仑
西昆仑
那就是说虚表的产生依赖构造函数,可是构造函数时虚函数,却还要去找虚表,确实是本末倒置了。
1
hikari
hikari
inline, static, constructor三种函数都不能带有virtual关键字。

inline是编译时展开,必须有实体;
static属于class自己的,也必须有实体;
virtual函数基于vtable(内存空间),constructor函数如果是virtual的,调用时也需要根据vtable寻找,但是constructor是virtual的情况下是找不到的,因为constructor自己本身都不存在了,创建不到class的实例,没有实例,class的成员(除了public static/protected static for friend class/functions,其余无论是否virtual)都不能被访问了。
西昆仑
西昆仑
“inline是编译时展开,必须有实体 static属于class自己,也必须有实体”,这句话看不懂,inline函数确实是编译时展开,但是必须有实体什么意思?static函数也确实是属于类自己,也提到了实体,这个实体是什么意思呢。
0
涧水潺湲
楼上厉害,受益匪浅。
0
换大米

这东西貌似可以倒着推出结论,且不管虚函数表这个概念,因为虚函数是为了解决运行期间绑定的问题,那么除了这个之外都不可用成为虚函数。构造函数等等诸如此类都是在编译期间就确定下来了,在运行期间是是改变不了的。那么自然不能为虚函数。

诸如此类吧,继续讨论 

0
Windoze
Windoze

引用来自“hikari”的答案

inline, static, constructor三种函数都不能带有virtual关键字。

inline是编译时展开,必须有实体;
static属于class自己的,也必须有实体;
virtual函数基于vtable(内存空间),constructor函数如果是virtual的,调用时也需要根据vtable寻找,但是constructor是virtual的情况下是找不到的,因为constructor自己本身都不存在了,创建不到class的实例,没有实例,class的成员(除了public static/protected static for friend class/functions,其余无论是否virtual)都不能被访问了。

inline和virtual没关系,表乱讲的说。

加了inline并不代表函数一定要编译期展开,编译器不高兴可以不展;加了virtual不代表函数只能通过虚表间接调用,上下文确定了可以直接调。

inline是一个“建议”,你可以在一些确定没办法inline的函数上加inline修饰,但是编译器会忽略,比如说递归函数肯定没法inline,但是用inline定义的递归函数不是错误。

virtual是告诉编译器为这个函数生成虚表中的入口,但是并不代表调用这个函数必须通过这个入口,如果上下文确定,编译器完全可以跳过虚表直接调用,甚至内联展开,例如:

class A {
public:
    inline virtual void f() {}
};

int main() {
    A a;
    a.f();
}

像样一点的编译器都不会通过虚表调用f(),更像样一点的还会展开inline

准确的说,还有一种成员不能加virtual,那就是成员模板,例如:

class A {
    template<typename T>
    virtual void f(T t){}
};

这段程序就是错的

言次
言次
完全同意这货的说法+1
c
code.monkey
我完全同意这货的说法
莫肖
莫肖
很有见地的说
0
CodeSun
CodeSun
类属static函数,构造函数s,inline函数(请参考《effective c++》p.136),模板成员函数不能virtual,这个比较麻烦,但是它也是由inline造成的,还有一个原因(详见《Think in C++》第二卷第五章),由于存在virtual成员模板函数,我们必须提前知道虚函数表的size大小,这就麻烦了。当然你可以理解为在编译时展开或者是替换的函数与virtual的运行时机制冲突
0
CodeSun
CodeSun

引用来自“Windoze”的答案

引用来自“hikari”的答案

inline, static, constructor三种函数都不能带有virtual关键字。

inline是编译时展开,必须有实体;
static属于class自己的,也必须有实体;
virtual函数基于vtable(内存空间),constructor函数如果是virtual的,调用时也需要根据vtable寻找,但是constructor是virtual的情况下是找不到的,因为constructor自己本身都不存在了,创建不到class的实例,没有实例,class的成员(除了public static/protected static for friend class/functions,其余无论是否virtual)都不能被访问了。

inline和virtual没关系,表乱讲的说。

加了inline并不代表函数一定要编译期展开,编译器不高兴可以不展;加了virtual不代表函数只能通过虚表间接调用,上下文确定了可以直接调。

inline是一个“建议”,你可以在一些确定没办法inline的函数上加inline修饰,但是编译器会忽略,比如说递归函数肯定没法inline,但是用inline定义的递归函数不是错误。

virtual是告诉编译器为这个函数生成虚表中的入口,但是并不代表调用这个函数必须通过这个入口,如果上下文确定,编译器完全可以跳过虚表直接调用,甚至内联展开,例如:

class A {
public:
    inline virtual void f() {}
};

int main() {
    A a;
    a.f();
}

像样一点的编译器都不会通过虚表调用f(),更像样一点的还会展开inline

准确的说,还有一种成员不能加virtual,那就是成员模板,例如:

class A {
    template<typename T>
    virtual void f(T t){}
};

这段程序就是错的

建议你看下C++98标准,把inline弄明白再说。你的这种说法无非是无效的inline函数,有效的inline是在编译时展开,这点毫无疑问(《effective c++》第30款),当然你加inline无效的话编译器也不会报错(一般有警告)。假若但就能否通过编译来讲,在这儿讨论这个还有意义吗?
0
Windoze
Windoze

引用来自“CodeSun”的答案

引用来自“Windoze”的答案

引用来自“hikari”的答案

inline, static, constructor三种函数都不能带有virtual关键字。

inline是编译时展开,必须有实体;
static属于class自己的,也必须有实体;
virtual函数基于vtable(内存空间),constructor函数如果是virtual的,调用时也需要根据vtable寻找,但是constructor是virtual的情况下是找不到的,因为constructor自己本身都不存在了,创建不到class的实例,没有实例,class的成员(除了public static/protected static for friend class/functions,其余无论是否virtual)都不能被访问了。

inline和virtual没关系,表乱讲的说。

加了inline并不代表函数一定要编译期展开,编译器不高兴可以不展;加了virtual不代表函数只能通过虚表间接调用,上下文确定了可以直接调。

inline是一个“建议”,你可以在一些确定没办法inline的函数上加inline修饰,但是编译器会忽略,比如说递归函数肯定没法inline,但是用inline定义的递归函数不是错误。

virtual是告诉编译器为这个函数生成虚表中的入口,但是并不代表调用这个函数必须通过这个入口,如果上下文确定,编译器完全可以跳过虚表直接调用,甚至内联展开,例如:

class A {
public:
    inline virtual void f() {}
};

int main() {
    A a;
    a.f();
}

像样一点的编译器都不会通过虚表调用f(),更像样一点的还会展开inline

准确的说,还有一种成员不能加virtual,那就是成员模板,例如:

class A {
    template<typename T>
    virtual void f(T t){}
};

这段程序就是错的

建议你看下C++98标准,把inline弄明白再说。你的这种说法无非是无效的inline函数,有效的inline是在编译时展开,这点毫无疑问(《effective c++》第30款),当然你加inline无效的话编译器也不会报错(一般有警告)。假若但就能否通过编译来讲,在这儿讨论这个还有意义吗?

如果这儿讨论的不是能否通过编译的话,那是在讨论什么呢?

返回顶部
顶部