[C++]static, virtual, const关键字

来源:互联网 发布:it行业怎么学 编辑:程序博客网 时间:2024/04/30 09:09

Static成员函数能声明为const吗?

不能。static成员函数是类所有的,可以直接用类名访问,不属于任何一个对象,访问时不会带上this指针。

但是const成员函数,访问时会添加一个隐式的const this*, 二者的用法是冲突矛盾的。


static成员函数能声明为virtual吗?

不能。静态成员函数对于每个类来说只有一份代码,没有多态绑定的必要性。


为什么C++不支持构造函数为虚函数?

虚函数通过虚指针vptr来访问,vptr通过构造函数来初始化。如果构造函数是虚函数,也就是产生无法产生vptr,将陷入悖论。


http://legendtkl.com/2014/09/30/cplusplus-static-and-virtual/

首先声明一下,这篇不是入门教程,前提是你了解了static的作用以及C++中的virtual函数的实现机制。:)

先来几个问题,大家思考一下,类的析构函数可以为virtual吗?类的构造函数可以为virtual吗?类的构造函数可以为static吗?类的析构函数可以为static吗?virtual函数可以为static吗?

static关键字的作用可以简单概括为:应用于全局变量时候,应用于局部变量的时候,应用于函数的时候以及在C++中应用于类的时候。前面三种写的都比较多,我今天主要写下static应用在C++类的时候,以及virtual关键字。

static在类中的运用简单来说两种:静态数据成员和静态成员函数。

静态数据成员被编译器提出与class之后,并被视为一个global变量,但只在class生命范围内可见。每个静态数据成员只有一个实体,存放在程序的数据段之中。可以用static修饰局部变量来对比理解。访问虽然可以通过’.’运算符来调用,但是并不是必须的,也就是说可以通过作用域操作符”::”来调用,比如class A有一个静态数据成员z可以A::z调用。值得注意的一点是,类的静态数据成员要在全局下进行定义,才能使用。否则编译器会报错:undefined reference。也就是说,静态成员在class中声明,外部是看不见的。

在说静态成员函数之前,要说下成员函数的调用方式。先说非静态成员函数吧。为了保证非静态成员函数至少和一般的外部函数有相同的效率,编译器会对非静态成员函数进行改写为一般的函数。主要有以下转化步骤:

  1. 改写函数原型,安插一个额外的参数this指针,用以提供一个存取管道,使类对象得以调用该函数。
  2. 对函数体中类的非静态数据成员的存取操作,改为经由this指针来存取。
  3. 将成员函数重新写成一个外部函数,对函数名称进行“mangling”处理,避免符号冲突。
    举个例子如下:
    123456789101112131415161718192021
    class Legend{private:    int x;    int y;    int z;public:    legend(){}    ~legend(){}    int sum() const{        int ret = x+y+z;        return ret;    }};//内部转化void sum_3legendFv(const legend *const this, int &__result){    int ret = this->x+this->y+this->z;    __result = ret;    return;}

感受一下思想,其中引入了NRV(named returned value),先不要太在意,知道有这个东西就好了。

虚成员函数是通过虚函数表来访问的。虚函数的实现机制简单来说就是,类为所有的虚函数建一个虚函数表vtbl,每个类只有一个。每个对象有一个虚函数表指针,vptr,通过指针来访问。因为访问说到底就是找到函数地址。之所以多态无法在编译器间确定,而要在运行时才能确定就是因为在编译器无法确定指针或者引用指向的真正的类型,从而无法确定偏移找到函数地址。

123
prt->normal()//内部转化(*ptr->vptr[1])(ptr)

其中vptr表示由编译器产生的指针,指向virtual table。1是virtual table slot的索引值,关联到normal函数,第二个ptr是this指针。

静态成员函数并不会关联到this指针,因此差不多等同于外部函数。主要有下面三个特性

  1. 不能直接存取其class内的非静态成员
  2. 不能够直接声明为const
  3. 不需要通过对象来调用。
    下面我们来看上面几个问题

1.类的析构函数可以为virtual吗?毋庸置疑,对于可能作为基类的类的析构函数要求就是virtual的。因为如果不是virtual的,派生类析构的时候调用的是基类的析构函数,而基类的析构函数只要对基类部分进行析构,从而可能导致派生类部分出现内存泄漏问题。

2.类的构造函数可以为virtual吗?答案也是不能的,通过上面虚函数的调用方式我们知道虚函数是通过vptr来访问的。那么vptr是怎么来的呢?vptr确定通过构造函数来初始化的。鸡生蛋,蛋生鸡,鸡生蛋……

3.类的构造函数可以为static吗?根据之前说的static不能访问非静态成员变量这点可以知道构造函数是不可以为static的。两者static是对应于每个类的,而构造函数主要负责初始化对象的。这里要提一下C#中的static构造函数是用于在使用类之前进行相关的初始化工作。比如,初始化静态成员或执行特定操作。CLR在第一次创建该类对象或调用该类静态方法时自动调用静态构造函数。

4.类的析构函数可以为static吗?同上。

5.virtual函数可以为static吗?答案是不可以。virtual函数和static函数访问方式是不一样。


内联函数,构造函数,静态函数都不能是虚函数

http://blog.csdn.net/love_gaohz/article/details/7534140

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)都不能被访问了。

 

 C++函数中那些不可以被声明为虚函数

常见的不不能声明为虚函数的有:普通函数(非成员函数);静态成员函数;内联成员函数;构造函数;友元函数。

1.为什么C++不支持普通函数为虚函数?

普通函数(非成员函数)只能被overload,不能被override,声明为虚函数也没有什么意思,因此编译器会在编译时邦定函数。

2.为什么C++不支持构造函数为虚函数?

这个原因很简单,主要是从语义上考虑,所以不支持。因为构造函数本来就是为了明确初始化对象成员才产生的,然而virtual function主要是为了再不完全了解细节的情况下也能正确处理对象。另外,virtual函数是在不同类型的对象产生不同的动作,现在对象还没有产生,如何使用virtual函数来完成你想完成的动作。(这不就是典型的悖论)

3.为什么C++不支持内联成员函数为虚函数?

其实很简单,那内联函数就是为了在代码中直接展开,减少函数调用花费的代价,虚函数是为了在继承后对象能够准确的执行自己的动作,这是不可能统一的。(再说了,inline函数在编译时被展开,虚函数在运行时才能动态的邦定函数)

4.为什么C++不支持静态成员函数为虚函数?

这也很简单,静态成员函数对于每个类来说只有一份代码,所有的对象都共享这一份代码,他也没有要动态邦定的必要性。

5.为什么C++不支持友元函数为虚函数?

因为C++不支持友元函数的继承,对于没有继承特性的函数没有虚函数的说法

*********************************************************************

1.顶层函数:多态的运行期行为体现在虚函数上,虚函数通过继承方式来体现出多态作用,顶层函数不属于成员函数,是不能被继承的。

2.构造函数:(1)构造函数不能被继承,因而不能声明为virtual函数

                        (2)构造函数一般是用来初始化对象,只有在一个对象生成之后,才能发挥多态作用,如果将构造函数声明为virtual函数,则表现为在对象还没有生成的情况下酒使用了多态机制,因而是行不通的,如下例:

#include<iostream>
using namespace std;
class B
{
public:

B() {}
virtual void show() {cout<<"***"<<endl;}
};
class D:public B
{
public:

D() {}
void show() {cout<<"==="<<endl;}
};

int main()
{
B *pb;
D d; //先生成对象
pb=&d;
pb->show(); //再体现多态
pb=new D(); //先调用构造函数

pb->show(); //再多态
delete pb;
return 0;
}

3.static函数:不能被继承,只属于该类。

4.友元函数:友元函数不属于类的成员函数,不能被继承。

5.inline函数:inline函数和virtual函数有着本质的区别,inline函数是在程序被编译时就展开,在函数调用处用整个函数体去替换,而virtual函数是在运行期才能够

确定如何去调用的,因而inline函数体现的是一种编译期机制,virtual函数体现的是一种运行期机制。此外,一切virtual函数都不可能是inline函数。


0 0