C++编程法则 静态成员函数和非静态成员函数之间的相互调用
Chapter1 C++静态成员函数和非静态成员函数之间的相互调用
一直对C++静态成员函数和非静态成员函数之间的相互调用记不住,都是死记硬背,今天突然醍醐灌顶;
1、静态成员函数不能调用非静态成员函数,因为静态成员函数属于类,没有this指针,而普通成员函数的调用需要this指针,所以,静态成员函数不能调用非静态成员函数;
2、普通成员函数可以调用静态成员函数,因为静态成员函数放在静态区,整个类和类对象都可以访问。
需要注意的是:静态数据成员不能在类中初始化,一般在类外和main()函数之前初始化,缺省时初始化为0。
1.static成员的所有者是类本身,但是多个对象拥有一样的静态成员。从而在定义对象是不能通过构造函数对其进行初始化。
2.静态成员不能在类定义里边初始化,只能在class body外初始化。
3.静态成员仍然遵循public,private,protected访问准则。
4.静态成员函数没有this指针,它不能返回非静态成员,因为除了对象会调用它外,类本身也可以调用
静态成员属于全局变量,是所有实例化以后的对象所共享的,而成员的初始化你可以想象成向系统申请内存存储数据的过程,显然这种共有对象。不能在任何函数和局部作用域中初始化。
class point{
public:
point(){};
// ...
private:
static int x,y;
};
// 类外初始化,不必再加static关键字
int point::x = 0;
int point::y = 0;
int main(){
// ...
}
为什么?
因为静态成员属于整个类,而不属于某个对象,如果在类内初始化,会导致每个对象都包含该静态成员,这是矛盾的。
什么东西能在类内初始化
能在类中初始化的成员只有一种,那就是静态常量成员。
这样不行
class A
{ private: static int count = 0; // 静态成员不能在类内初始化 };
这样也不行
class A
{ private: const int count = 0; // 常量成员也不能在类内初始化 };
但是这样可以
class A
{ private: static const int count = 0; // 静态常量成员可以在类内初始化 };
结论:
- 静态常量数据成员可以在类内初始化(即类内声明的同时初始化),也可以在类外,即类的实现文件中初始化,不能在构造函数中初始化,也不能在构造函数的初始化列表中初始化;
- 静态非常量数据成员只能在类外,即类的实现文件中初始化,也不能在构造函数中初始化,不能在构造函数的初始化列表中初始化;
- 非静态的常量数据成员不能在类内初始化,也不能在构造函数中初始化,而只能且必须在构造函数的初始化列表中初始化;
- 非静态的非常量数据成员不能在类内初始化,可以在构造函数中初始化,也可以在构造函数的初始化列表中初始化;
为什么static数据成员一定要在类外初始化
这是因为被static声明的类静态数据成员,其实体远在main()函数开始之前就已经在全局数据段中诞生了(见《Inside The C++ Object Model》page247)!其生命期和类对象是异步的,(而且静态语意说明即使没有类实体的存在,其静态数据成员的实体也是存的)这个时候对象的生命期还没有开始,如果你要到类中去初始化类静态数据成员,让静态数据成员的初始化依赖于类的实体,,那怎么满足前述静态语意呢?难道类永远不被实例化,我们就永远不能访问到被初始化的静态数据成员吗
静态成员变量隶属于类,不是某个对象,所以静态成员变量不可能占用某一个对象的存储空间,所以静态成员需要再类外部定义,以便静态成员变量在全局数据区分配其存储空间。
Chapter2 类的静态成员函数调用类非静态成员的方法(传入类指针)
原文链接:https://blog.csdn.net/u010810750/article/details/81873408
在类中使用静态成员函数是一种破坏封装的行为,因为静态成员函数只能调用类的静态成员。但是在有些情况下只能使用静态成员函数,比如类内绑定自身成员函数作为回调函数,这种情况在开启多线程时很常见,如果不想将回调定义为全局那只能定义为类静态了,为了避免过度破坏封装类中应当尽量不要让类静态成原函数调用类成员。这种情况下可以用一种比较取巧的方法。
因为类的静态成员和普通成员其实就一种区别,那就是静态成员本身没有this指针,所以静态成员属于类而不属于类对象。如果我们想在类的静态成员函数里面调用类的普通成员,只需要把类指针当做参数传入静态成员函数里面,静态成员函数可以使用这个指针调用类的普通成员,demo如下:
#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std;
class A
{
private:
string str;
public:
void handler(string _str)
{
str = _str;
cout<<str<<endl;
}
static void handler_static(void *p_A,string _str)
{
A *_p = static_cast<A*>(p_A);
_p->handler(_str);
}
void start()
{
void *p = this;
string _str = "static";
handler_static(p,_str);
}
};
int _tmain(int argc, _TCHAR* argv[])
{
A a;
a.start();
getchar();
return 0;
}
handler_static为静态成员函数,入参有两个,第一个为本身对象this指针,另一个为实际需要传入的参数。在这里实现的功能为使用类的静态成员函数给类的普通成员变量str赋值,如果不采用这种传入this指针间接调用的方式,那str也必须定义为静态的。 当然在这里看起来没多大意义,只是用一个handler成员函数接收this指针间接实现了功能。 但是在代码复杂的情况下这种方式可以避免过度破坏封装,在必须使用静态的地方(比如绑定回调)才定义静态成员,就不会有静态成员使用的连锁反应了。
Chapter3 静态成员、单例模式、友元、常量成员
Chapter4 C++ 静态成员函数
原文链接:https://blog.csdn.net/feng19870412/article/details/124902096
成员函数也可以定义为静态的,在类中声明函数的前面加static就成了静态成员函数,例如:
//定义静态成员函数;
static long long get_number(){
return number;
}
和静态成员变量一样,静态成员函数是类的一部分,而不是对象的一部分。如果要在类外调用公用的静态成员函数,要用类名和域运算符“::”,例如:
student::get_number();
静态成员变量和静态成员函数,是属于类。那么,当用类来定义一个对象之后,静态成员变量和静态成员函数也属于对象,所以,也允许通过对象名调用静态成员函数。如下是程序测试代码:
程序运行结果如下:
可以看到,student类定义了get_number ()静态成员函数,那么,可以通过对象stud访问,也可以直接通过类名student来访问。
注意:当调用一个对象的成员函数(非静态成员函数)时,系统会把该对象的起始地址赋给成员函数的this指针。而静态成员函数并不属于某一个对象,它与任何对象都无关,因此静态成员函数没有this指针,既然它没有指向某一对象,就无法对一个对象中的非静态成员进行默认访问(即在引用成员变量时不指定对象名)。
可以说,静态成员函数与非静态成员函数的根本区别是:非静态成员函数有this指针,而“静态成员函数没有this指针”,由此决定了静态成员函数不能访问本类中的非静态成员。
静态成员函数可以直接引用本类中的静态成员变量,静态成员变量同样是属于类的,可以直接引用。在C++程序中,静态成员函数主要是用来访问静态成员变量,而不访问非静态成员。如下是一个例子,声明了volume(); 函数是 static 类型。那么,在该函数中只能够访问static类型的成员变量。
class Box
{
public:
static int height; // 定义为静态成员变量
int width;
int length;
Box(int, int);
static int volume(); //静态成员函数
};
int Box::volume() //是static 类型的函数
{
cout << height <<endl; //合法,因为height 是static类型
cout << width << endl; //非法,因为width 不是static类型
}
但是,并不是绝对不能引用本类中的非静态成员,只是不能进行默认访问,因为无法知道应该去找哪个对象。如果一定要引用本类的非静态成员,应该加对象名和成员运算符“.”,例如:
Box a(16, 11); //定义一个对象a
int Box::volume() //是static 类型的函数
{
cout << a.width << endl; //a 在这是定义的一个全局对象,在volume()函数中,可以访问指定的 a 对象的非静态成员变量。
return 0;
}
只要 a 是一个已经定义的 Box 类对象,而且,它的作用域在 volume(); 内,那么,这个语句就合法。
总结
学习了静态成员变量和静态成员函数,我们了解到static关键字修饰的成员变量和成员函数,就属于“静态”类型。就是属于当前类的属性。
所以,当我们定义一个C++类的时候,静态成员就已经存在,它是属于这个类。所以,就可以直接通过类名来访问静态成员。
当“编译程序”的时候,它们就存在的,所以编译程序完成之后,不用定义类对象,就可以访问类的静态成员变量和静态成员函数,程序测试如下:
程序运行结果如下:
可以看到,在main()函数中没有定义任何对象,直接通过student类名访问静态的print()函数,而且,在print()函数中访问静态的成员变量。
所以,对于静态成员变量和静态成员函数,它们是属于一个类的,那么编译程序的时候,就对类进行了编译处理,所以,编译完程序之后,就可以使用类名和作用域运算符“::”来调用。
而对于非静态的成员变量和成员函数,是由一个对象来确定的,所以,必须在定义对象之后,才能够调用它们。
静态成员变量和静态成员函数,都是属于类的,可以使用类名和作用域运算符“::”来调用它们。当定义了类对象之后,也可以如同普通的成员变量和成员函数一样,使用对象和点运算符“.”来调用它们。
我们可以以“物体出现的时间先后顺序”来推理理解:静态和非静态成员之间的调用关系。根据,C++程序被编译之后,类先被编译出来,然后,才可以使用类来定义对象。所以,类先出现在对象之前。
假设现在只编译了类,不定义对象,所以,就通过类名来操作静态成员变量静态成员函数。如果此时,在静态成员函数中操作了非静态的成员变量,则出错,因为,非静态的成员变量是属于一个对象的,而此时,我们还没有使用类来定义对象,没有任何一个对象存在,所以,我们定义的静态成员函数,不能够调用非静态的成员变量和成员函数。
如果使用类来定义了一个对象,那么,这个对象的非静态成员变量和成员函数就产生了。而且,在编译了类之后,类的静态成员变量和静态成员函数已经生成,所以,可以使用对象的非静态成员函数来操作静态的成员变量和静态的成员函数。
读者要仔细分析,掌握好每一个知识点,然后,自己总结出对该知识点的理解。自己总结出来的知识,理解才深刻,才是自己掌握的知识。