目录
一、什么是静态属性?
在类型设计中,用关键字static修饰的数据成员为静态数据成员,由该类型所实例化的所有对象,共享系统为静态成员分配的一个存储空间,这个存储空间是程序执行main函数之前分配的。在实例化对象时不再为静态成员分配空间。也就是说静态成员数据不在对象空间中。
二、静态成员变量的特点与优势
1.静态成员变量特点
(1)使用静态成员变量的主要目的是信息共享。
设计一个圆类作为示例,代码如下:
#include<iostream>
using namespace std;
class Circle
{
private:
double pi;
double radius;//圆的半径
public:
Circle(double r = 0.0):pi(3.14),radius(r){}
~Circle(){}
double area()const
{
return pi * radius * radius;
}
};
int main()
{
Circle cir(12.23);
cir.area();
size_t sz = sizeof(cir);
return 0;
}
若想让圆类中所有对象都能使用这个pi值,可以将pi设计成静态数据成员:
class Circle
{
private:
static const double pi;
double radius;//圆的半径
public:
//Circle(double r = 0.0):pi(3.14),radius(r){}//error
Circle(double r =0.0):radius(r){}
~Circle(){}
double area()const
{
return pi * radius * radius;
}
};
const double Circle::pi = 3.14;
(注:大家可以发现,如果在构造函数初始化列表中定义静态数据成员会报错,这是因为静态数据成员为所有对象共有,在类内定义则每实例化一个对象都要定义一次静态成员变量 ,重复定义了。所以静态数据成员要在类外初始化。)
(2).静态成员变量先于任何对象的存在而存在。静态数据成员属于整个类型。
(3).静态数据成员在使用时,可以通过类型名直接访问,也可以通过类的对象访问(前提是可访问符为public),格式:类名::静态成员变量名 或 对象.静态成员变量名
(4).当在类的外部定义静态成员时,不能重复 static
关键字,该关键字只出现在类内部的声明语句。
(5)使用静态成员变量可以记录由同一类建立的对象的数量
设计一个猫咪类,代码示例:
class Cat
{
private:
static int num;
private:
string _owner;//猫主名
string _name;//猫名
public:
Cat(const string& own, const string& name) :_owner(own), _name(name)
{
num += 1;
cout << "Create Cat num:"<< num << endl;
}
~Cat()
{
num -= 1;
cout << "Destory Cat num:" << num << endl;
}
};
int Cat::num = 0;
(6)在类的成员函数中使用静态数据成员,静态数据成员前没有this指针修饰。
还用上述猫咪类型举例,在猫咪类型中加入print()方法:
class Cat
{
private:
static int num;
private:
string _owner;
string _name;
public:
Cat(const string& own, const string& name) :_owner(own), _name(name)
{
num += 1;
cout << "Create Cat:" << endl;
}
~Cat()
{
num -= 1;
cout << "Destory Cat:" << endl;
}
void print()const
{
cout << "Owner: " << _owner << "Name: " << _name << endl;
num += 1;
}
};
int Cat::num = 0;
我们发现静态数据成员num在const修饰的print()方法中可以实现+=操作,说明其没有被const限制,而const本质上修饰的是this指针,也就是说明了静态数据成员前没有this指针。
(7)静态属性的类型是int、char、short,并且前有const修饰时,可以在类内进行初始化。
class Bar
{
private:
static const int nameSize = 20;
//static const char namesize[nameSize] = "Xiao wang Bar";//error
};
(8)静态数据成员的类型可以是其所属类,而非static数据成员只能被声明为该类的指针。
代码示例:
class Bar
{
private:
int a;
static Bar sm_bar;//ok
private:
Bar m_bar;//error
Bar* pr;//ok
};
只有在定义对象时,才会为对象的数据成员分配内存空间,只声明了类而没有定义对象,类的一般数据成员是不占空间的,而上面提到类的静态成员是系统提前单独为其分配好了内存,使用Bar定义对象时,首先需要知道该类型所需要的空间,所以在定义一个Bar对象sm_bar时, 类型所需要的空间只需要计算a所需要的空间即可,是一个可计算的值。
直接定义Bar对象m_bar时,可以认为编译器需要计算int+Bar的大小,但无法确定Bar类型大小,所以报错,非static数据成员只能被声明为该类的指针。
2.静态成员变量优势
同全局变量相比,使用静态成员有两个优势:
(1)静态数据成员没有进入程序全局名字空间,因此不会存在与程序中其他全局名字冲突的可能
(2)可以实现信息隐藏,静态数据成员可以是private成员,而全局变量不能。
三、静态方法
函数成员说明为静态,将与该类不同对象无关,与静态数据成员不同的是,为了方便使用,静态函数成员多为公有。
普通成员函数在参数传递时编译器会隐藏地传递一个this指针,通过this指针来确定调用类产生的哪个对象,而静态函数没有this指针,不能通过静态函数访问普通变量,代码示例:
#include<iostream>
using namespace std;
class Object
{
private:
static int num;
int value;
public:
Object(int val = 0) :value(val) {}
~Object() {}
void print()
{
cout << "value: " << value << "num: " << num << endl;
}
static void show() {
cout << "value: " << value << "num: " << num << endl;//error,报错
}
static void fun(Object& obj)//不能加const,不能定义成常方法
{
cout << obj.value << " " << num << endl;
}
};
int Object::num = 0;
show()是静态成员函数,没有this指针, 不能确定访问哪个对象中的value,所以会报错。并且静态函数成员不能定义成常方法。
四、继承与静态成员
静态成员只有一个,所有对象共享,包括基类和派生类对象。
例:设计一个Person类作为基类,派生出Student学生类和职工Employee类,代码如下
#include<iostream>
using namespace std;
class Person
{
protected:
static int num;
public:
Person(){}
~Person(){}
};
int Person::num = 0;
class Student:public Person
{
public:
Student() { cout << "Create Student: " << (num += 1) << endl; }
~Student() { cout << "Destory Student: " << (num -= 1) << endl; }
};
class Employee:public Person
{
public:
Employee() { cout << "Create Employee: " << (num += 1) << endl; }
~Employee() { cout << "Destory Employee: " << (num -= 1) << endl; }
};
int main()
{
Student s1, s2, s3;
Employee e1, e2;
return 0;
}
运行结果:
Person基类中设计了静态成员num,在基类外初始化,整个继承体系中只有一个静态成员num,无论派生出多少个子类,都只存在一个num静态成员实例。