C++面向对象:静态成员和静态类
1.类的静态成员
1.1 static关键字
static
关键字在C
语言中有2种用法:static
修饰局部变量和全局变量。这两种用法的含义完全不同,一点关系都没有。static
关键字在C++
中扩展了第3种用法:静态类、静态成员,含义也和前2种完全不同。
1.2 什么是静态成员
在一个class
内用static
修饰成员变量,则被修饰的成员变量即为静态成员变量,用static
修饰成员方法,则被修饰的成员方法即为静态成员方法。
静态成员属于class
本身,而不属于它定义出来的对象。
1.3 静态成员的特征和实践验证
#include<iostream>
#include<string>
using namespace std;
class person
{
public:
int age; //普通成员变量
static int nums; //静态成员变量
};
- 特征1:静态成员变量可以在类的多个对象中访问,但是要在类外声明。不同对象访问的其实是同一个实体,静态成员变量被多个对象共享。
int person::nums = 1;//通过对象除了要在类外定义
int main(int argc,char**argv)
{
person p1,p2;
cout<<p1.nums<<" "<<p2.nums<<endl;
p1.nums=10;
cout<<p1.nums<<" "<<p2.nums<<endl;
return 0;
}
输出:
1 1
10 10
- 特征2: 静态成员变量和方法也可以根本不产生对象而用类本身来调用,语法是
类名::静态成员变量
。
int person::nums = 1;//通过对象除了要在类外定义
int main(int argc,char**argv)
{
cout<<person::nums<<endl;
person::nums = 10;
cout<<person::nums<<endl;
return 0;
}
输出:
1
10
- 特征3:静态成员函数在类外实现时只需在类内声明时加
static
,类外实体无须加static
关键字,否则是错误的(因为在类外实体前面加static
会按照static
修饰全局函数类理解)。
class person
{
public:
int age; //普通成员变量
static int nums; //静态成员变量
static void print(void);
};
//static void person::print(void) 错误写法
void person::print(void) //正确写法
{
cout<<"print()."<<endl;
}
2.静态成员的深度理解
2.1静态数据成员的使用
静态数据成员不能在类中初始化,因为类定义实际只是模型,本身并没有和变量/对象实体去关联。
静态数据成员不能在类的构造函数中初始化,因为构造函数是用来构造具体单个对象的,而静态成员属于类(或者说类和他的所有对象共享),如果在构造函数中允许对静态成员初始化或赋值,就会每多创建一个对象,原有对象中该静态成员的值莫名其妙变了,这是不合理的。当然静态数据成员不能用初始化列表方式来初始化。
静态数据成员如果不初始化则值默认为0,并且仍然遵循public
,private
,protected
访问准则。
2.2 静态成员和普通成员的互相调用规则
规则1:普通成员函数中可以调用静态成员变量和方法,调用方法有3种:
- 1.直接访问
- 2.
this
指针访问- 3.
类名::func()
方式访问
class person
{
public:
int age; //普通成员变量
static int nums; //静态成员变量
static void print(void);
void func(void)
{
//print(); 方法1
//this->print();方法2
person::print();//方法3
}
};
void person::print(void)
{
cout<<"static function print()."<<endl;
}
规则2:静态方法中只能访问静态成员变量和方法,不能访问任何非静态的东西,否则会编译报错。
class person
{
public:
int age; //普通成员变量
static int nums; //静态成员变量
static void print(void);
};
void person::print(void)
{
this->age=10;
}
编译报错:
static.cpp: In static member function ‘static void person::print()’:
static.cpp:22:2: error: ‘this’ is unavailable for static member functions
this->age = 10;
规则3:静态方法中如果确实需要访问非静态成员,应该通过函数传参方式。
2.3 从内存角度来看静态成员
class
定义时只是定义类型(一种模型),并不定义变量和对象,静态成员变量真正定义是在外部,类似于全局变量。
静态成员变量在链接时分配,程序加载时被落实到内存中,程序结束时死亡,它的规则是等同于全局变量的。静态成员函数在编译链接时分配,程序加载时被落实到内存中,程序结束时死亡,类似于全局函数。
普通成员和对象是绑定的,随对象的创建和释放而生死(不管在栈里还是堆里),类似于局部变量和malloc
堆内存。
静态成员变量在对象中不占用存储空间,其实是放在全局变量空间里的。
3.静态成员的用途
3.1 用途举例
静态数据成员的用途之一是统计有多少个对象实际存在,比如声明一个人类,其中一个成员为人的总数,则这个变量就应当声明为静态变量,应该根据实际需求来设置成员变量。
#include<iostream>
#include<string>
using namespace std;
class person
{
public:
int age; //普通成员变量
static int nums; //静态成员变量
person(){person::nums++;}
~person(){person::nums--;}
};
int person::nums = 0;//通过对象除了要在类外定义
int main(int argc,char**argv)
{
person a,b,c;
cout<<person::nums<<endl;
return 0;
}
输出:
3
静态方法就是与该类相关的,是类的一种行为,而不是与该类的实例对象相关。
3.2 静态成员与面向对象
静态成员仍然在class
内,仍可通过对象调用,因此表面上遵守面向对象规则。但是静态成员一定程度上破坏了面向对象,因为没有对象直接用class
名也可以调用静态成员,但是这种破坏提升了效率。
静态成员可被看做是类外部的全局变量和全局函数被封装到了类的内部,这是为了类内方便访问这个变量。一个类的静态成员和非静态成员是完全不同的,两者唯一的关联可能就是隶属于同一个class
的作用域内。
4.静态类的概念
4.1 什么是静态类
静态类内部的成员全是静态成员,没有非静态成员。一些高级语言中在class
声明时使用static
修饰来表示整个类是个静态类,C++
不支持这种语法,但是可以实现为所有成员都是静态来达到静态类的效果。
静态类不能被实例化,因为所有成员都是静态的,定义对象实际上就是实例化里面的非静态成员,但是静态类并没有非静态成员,所以不用实例化。
静态类是密封(sealed
)的。所谓是密封,就是不可被继承,不能拿来做父类。静态类不包括构造函数,因为构造函数就是在实例化对象是调用的,但是静态类又不能被实例化。静态类不能指定任何接口实现,不能有任何实例成员。
静态类的成员不能有protected
或protected internal
访问保护修饰符。静态类不能包含构造函数,但仍可声明静态构造函数以分配初始值或设置某个静态状态。
4.2 静态类的优势
编译器能够执行检查以确保不致偶然地添加实例成员。编译器将保证不会创建此类的实例。
静态类是密封的,因此不可被继承。例如当我们需要实现一些只包含方法,各种要用的函数的类时,就可以实现为静态类。
4.3 C++不支持静态类
Java
、C#
等高级语言支持静态类,而C++
并不支持。C++
中创建静态类与创建仅包含静态成员和私有构造函数(把构造函数放在private
里)的类大致一样,私有构造函数可以阻止类被实例化。