为什么虚函数必须是非静态成员函数 构造函数能为static吗?
静态函数
用static声明的函数是静态函数。静态函数可以分为全局静态函数和类的静态成员函数。
Static关键字
在类中,用static声明的成员变量为静态成员变量,它为该类的公用变量,在第一次使用时被初始化,对于该类的所有对象来说,static成员变量只有一份。
用static声明的方法是静态方法,在调用该方法时,不会将对象的引用传递给它,所以在static方法中不可访问非static的成员。
静态方法不再是针对于某个对象调用,所以不能访问非静态成员。
可以通过对象引用或类名(不需要实例化)访问静态成员
C++类静态数据成员与类静态成员函数
函数调用的结果不会访问或者修改任何对象(非static)数据成员,这样的成员声明为静态成员函数比较好。且如果static int func(....)不是出现在类中,则它不是一个静态成员函数,只是一个普通的全局函数,只不过由于static的限制,它只能在文件所在的编译单位内使用,不能在其它编译单位内使用。
静态成员函数的声明除了在类体的函数声明前加上关键字static,以及不能声明为const或者volatile之外,与非静态成员函数相同。出现在类体之外的函数定义不能制定关键字static。
静态成员函数没有this指针。
在没有讲述本章内容之前如果我们想要在一个范围内共享某一个数据,那么我们会设立全局对象,但面向对象的程序是由对象构成的,我们如何才能在类范围内共享数据呢?
这个问题便是本章的重点:声明为static的类成员或者成员函数便能在类的范围内共同享,我们把这样的成员称做静态成员和静态成员函数。
下面我们用几个实例来说明这个问题,类的成员需要保护,通常情况下为了不违背类的封装特性,我们是把类成员设置为protected(保护状态)的,但是我们为了简化代码,使要说明的问题更为直观,更容易理解,我们在此处都设置为public。
以下程序我们来做一个模拟访问的例子,在程序中,每建立一个对象我们设置的类静态成员变自动加一,代码如下:
- #include <iostream>
- using namespace std;
- class Internet
- {
- public:
- Internet(char *name,char *address)
- {
- strcpy(Internet::name,name);
- strcpy(Internet::address,address);
- count++;
- }
- static void Internet::Sc()//静态成员函数
- {
- cout<<count<<endl;
- }
- Internet &Rq();
- public:
- char name[20];
- char address[20];
- static int count;//这里如果写成static int count=0;就是错误的
- };
- Internet& Internet::Rq()//返回引用的成员函数
- {
- return *this;
- }
- int Internet::count = 0;//静态成员的初始化
- void vist()
- {
- Internet a1("中国软件开发实验室","www.cndev-lab.com");
- Internet a2("中国软件开发实验室","www.cndev-lab.com");
- }
- void fn(Internet &s)
- {
- cout<<s.Rq().count;
- }
- void main()
- {
- cout<<Internet::count<<endl;//静态成员值的输出
- vist();
- Internet::Sc();//静态成员函数的调用
- Internet b("中国软件开发实验室","www.cndev-lab.com");
- Internet::Sc();
- fn(b);
- cin.get();
- }
上面代码我们用了几种常用的方式建立对象,当建立新对象并调用其构造函数的时候,静态成员cout便运行加1操作,静态成员的初始化应该在主函数调用之前,并且不能在类的声明中出现,通过运行过程的观察我们发现,静态成员count的状态并不会随着一个新的对象的新建而重新定义,尽而我们了解到类的静态成员是属于类的而不是属于哪一个对象的,所以静态成员的使用应该是类名称加域区分符加成员名称的,在上面的代码中就是Internet::count,虽然我们仍然可以使用对象名加点操作符号加成员名称的方式使用,但是不推荐的,静态类成员的特性就是属于类而不专属于某一个对象。
静态成员函数的特性类似于静态成员的使用,同样与对象无关,调用方法为类名称加域区分符加成员函数名称,在上面的代码中就是Internet::Sc();静态成员函数由于与对象无关系,所以在其中是不能对类的普通成员进行直接操作的。
如果上面的 static void Internet::Sc()修改成为:
- static void Internet::Sc()//静态成员函数
- {
- cout<<name<<endl;//错误
- cout<<count<<endl;
- }
静态成员函数与普通成员函数的差别就在于缺少this指针,没有这个this指针自然也就无从知道name是哪一个对象的成员了。
根据类静态成员的特性我们可以简单归纳出几点,静态成员的使用范围:
1.用来保存对象的个数。
2.作为一个标记,标记一些动作是否发生,比如:文件的打开状态,打印机的使用状态,等等。
3.存储链表的第一个或者最后一个成员的内存地址。
为了做一些必要的练习,深入的掌握静态对象的存在的意义,我们以前面的结构体的教程为基础,用类的方式描述一个线性链表,用于存储若干学生的姓名,代码如下:
- #include <iostream>
- using namespace std;
- class Student
- {
- public:
- Student (char *name);
- ~Student();
- public:
- char name[30];
- Student *next;
- static Student *point;
- };
- Student::Student(char *name)
- {
- strcpy(Student::name,name);
- this->next=point;
- point=this;
- }
- Student::~Student ()//析构过程就是节点的脱离过程
- {
- cout<<"析构:"<<name<<endl;
- if(point==this)
- {
- point=this->next;
- cin.get();
- return;
- }
- for(Student *ps=point;ps;ps=ps->next)
- {
- if(ps->next==this)
- {
- cout<<ps->next<<""<<this->next<<endl;
- ps->next=next;//=next也可以写成this->next;
- cin.get();
- return;
- }
- }
- cin.get();
- }
- Student* Student::point=NULL;
- void main()
- {
- Student *c = new Student("marry");
- Student a("colin");
- Student b("jamesji");
- delete c;
- Student *fp=Student::point;
- while(fp!=NULL)
- {
- cout<<fp->name<<endl;
- fp=fp->next;
- }
- cin.get();
- }
- <span style="font-size:14px;">从上面的代码来看,原来单纯结构化编程需要的一个链表进入全局指针在这里被类的静态成员指针所替代(类的静态成员完全可以替代全局变量),这个例子的理解重点主要是要注意观察类成员的析构顺序,通过对析构顺序的理解,使用析构函数来进行节点的脱链操作。
- <strong><span style="color:maroon;">为什么虚函数必须是非静态成员函数</span></strong>
- 如果定义为虚函数,那么它就是动态绑定的,也就是在派生类中可以被覆盖的,这与静态成员函数的定义本身就是相矛盾的。
- ==
- 主要有两个作用:
- 1、管理静态数据成员;
- 2、提供类范围的功能,即不需要对象来实现的功能。
- <strong><span style="color:red;">比如Symbian</span>中的NewL/LC方法就是static的</strong>
- ==
- 使用static关键字声明的函数成员是静态的,静态成员函数同样也属于整个类,由同一个类的所有对象共同维护,为这些对象所共享。
- 作为成员函数,它的访问属性可以受到类的严格控制,对于公有的静态函数成员函数,可以通过类名或对象名来调用,但一般情况下建议用对象名来引用静态函数成员<strong><span style="color:blue;">(真的吗?)</span></strong>。注意,一般的成员函数只能通过对象名来调用。
- 由于一个类的静态成员函数只有一个拷贝,因此它访问对象的数据何函数使受到了限制。静态成员函数可以直接访问该类的静态数据成员。而<strong>访问非静态数据成员,必须通过参数传递方式得到对象名,然后通过对象名来访问</strong>。可以看到,通过静态函数成员访问非静态成员使相当麻烦的,一般的使用中,它主要用来访问全局变量或同一个类中的静态数据成员,特别是和后者一起使用,达到对同一个类中对象之间共享的数据进行维护的目的。
- <strong><span style="color:red;">构造函数不可以定义为static</span>,</strong>看了上面,应该可以理解原因。
- 注意,由于static不是函数类型中的一部分,所以在类定义之外定义静态成员函数时不使用static,在类中定义的静态成员函数是内联的。
- 一般来说,通过成员名限定比使用对象名访问静态成员要好。因为静态成员不是对象的成员。
- <strong><span style="color:red;">静态成员可以被继承</span></strong>,这时,基类对象和派生类的对象共享该静态成员,除此之外,在类等级中对静态成员的其他特性(例如,静态成员在派生类中的访问权限,在派生类中重载成员函数等)的分析与一般成员类似。
- <strong><span style="color:red;">静态成员函数不能被申明为虚函数</span></strong>,静态成员具有外部连接属性,static仅有的含义是使该成员为该类的所有对象共享。
- <strong>类中的任何成员函数都可以访问静态成员,但静态成员函数只能通过对象名(或指向对象的指针)访问该对象的非静态成员,因为静态成员函数没有this 指针。</strong></span><strong>
- <span style="color:#339966;">
- </span><strong><span style="font-size:14px;color:#ff6600;">虚函数必须是非静态成员函数</span></strong></strong><span style="color:#ff6600;">
- </span><strong><span style="font-size:14px;color:#ff6600;">构造函数不可以定义为static</span></strong>