有些难度了。尽量搞清楚!
静态类成员
引子
- 这是个什么东西呢?就是比如你在定义类的时候,私有成员有定义一个容器。但是,你为了程序运行效率,你在定义这个类的类对象的时候,你不想复制那个私有成员的容器,为嘛?要是容器长度太大了,复制起来占不占时间?
- 所以呢,你就把那个private:里定义的容器设置成静态类型的,举个例子:
class Triangular{
public:
//...
private:
static vector<int>_elems;
你看这个格式,我把私有成员的vector容器定义成静态类型的,也就是静态类成员,既然我不想复制容器,那我所有定义的类对象(Triangular类的类对象)是不是就共用这一个容器? 对吧,所以我们可以得知:静态数据成员用来表示唯一的,可共享的成员。它可以在同一类的所有对象中被访问。
- 那这样有什么好处呢?比如说你定义了一个静态数据成员(静态类成员)是个容器,这个容器因为是共用的,比如我想在某个类对象里查找容器里的一个元素,我直接找就完了,我还用复制容器或者我自己再手动往里头添元素?找不到,我就往里面填进去查找的元素呗(一般是知道这是个什么数列,查找的元素用户不会乱写,乱写有乱写的处理方法)。
- 好,那我再通过同一个类的类对象去查找容器里之前要查找的元素,这个元素在之前在容器里没有被找到,然后我不就添加进去了吗,这时候我再找,不就找到了?方便吗?我还用再复制一遍这个容器或者手动填元素再找吗?当然这只是一个例子,其实静态类成员还有其他用处和讲究。待我慢慢剖析讲解。
- 其实说到了这里,我认为静态类成员就像是放在qq群上的公共资源库,大家(类对象)随便提取或上传资源到这个库里,增删查改都行(局限性的增删查改,管理员可以随便操作哈哈哈)。所以我想定义成静态类成员一定要融会贯通这个举例,遵守规则(下面讲)
- 对于类而言,静态数据成员只有唯一的一份实体,所以我们必须在类的头文件附属的程序代码文件提供清楚的定义。静态数据成员的名称必须附上类作用域运算符,如:
vector<int>Triangular::_elems;
//在比如Triangular.cpp里写这两行代码
int Triangular::_initial_size=8;
//_initial_size是静态数据成员(静态类成员),
//我可以在类头文件的程序代码文件里给它初始化。
- 如果我想在类成员函数里访问静态数据成员,可以按照访问非静态数据成员一样访问静态数据成员:
if(_elems.size()<elem_cnt)
{
gen_elements(elem_cnt);
}
这里就访问了_elems这个静态数据成员(容器),访问了它的长度通过泛型算法.size()。
- 对于const型静态数据成员,必须在声明这个成员的时候给它赋定初值
class intBuffer{
public:
//...
private:
static const int _buf_Size=1024;//赋定初值
int _buffer[_buf_size];
静态成员函数
- 一般情形下,成员函数必须通过其类的类对象调用,同时这个类对象会被绑定到该成员函数的this指针(this指针指向其调用成员函数的类对象(房子里指向房子整体)。通过存储于每个类对象中的this指针,成员函数才能够访问存储于每个类对象中的非静态数据成员,嗯这点很重要,通过this指针,我们就可以访问类私有成员啦!
- 某些类成员函数并未访问类的非静态数据成员,执行这种函数不会影响类对象的什么。所以可以很方便的用非成员函数的方式(调用一般自定义函数的方式)来调用这种类成员函数,但是我必须在这种类成员函数前加类作用域运算符:
bool Triangular::
is_elem(int value)
{
if(!_elems.size()||_elems[_slems.size()-1]<value)
{
gen_elems_to_value(value);
//如果静态数据成员(容器里)
//是空的或者值大于了容器最后一个元素的值
//就把这个传进来的value扔进容器里(一般是push_back(value))
//到容器最后,数列嘛。
}
vector<int>::iterator found_it;
vector<int>::iterator end_it=_elems.end();
//泛型指针的定义,分别指向容器的被查找到的元素
//和容器的末尾元素的位置的下一个位置
//我可不可以定义在一起呢?少写一个vector<int>::iterator
//答案是,我的天呐,完全可以!我在cb编译器上写了
//也是容器类型的泛型指针(vector<string>型)的
//vector<string>::iteratorit=find(slist.begin(),slist.end(),sval),it22;
//虽然我没对it22作什么操作,但是编译器仍然通过
//并正常运行程序。
//泛型指针也可
//所以你就写成
//vector<int>::iterator found_it,end_it=_elems.end();
//found_it直接声明的时候赋初值都行。
//通过泛型算法给泛型指针赋值一个容器里被查找到的元素的地址
//(有可能没找到,此时赋值给这个泛型指针容器的末尾元素的位置的下一个位置地址。
return found_it!=end_it;
//巧妙的bool表达式,因为这个类成员函数返回类型是bool类型
//所以通过这样的关系运算符判断,可得知return true还是false
}
if(Triangular::is_elem(8))
{
//...
}//这样的方式调用并未访问任何非静态数据成员(非静态类成员)的成员函数
这样的方式调用,就能让编译器或者程序阅读者知道我们想调用哪个类的成员函数is_elem().
- 所以我们说静态成员函数可以在和对象没有任何关系的情形下(如不改对象里的其他数据成员的值)被调用。
- 注意,成员函数必须在不访问任何非静态成员的条件下才能被声明为static,否则把这种成员函数声明成static将毫无意义。声明静态成员函数的方式如下:
class Triangular{
public:
static bool is_elem(int);
static void gen_elemnts(int length);//声明函数时变量名可写可不写
static void gen_elems_to_value(int value);
static void display(int length,int beg_pos,ostream &os=cout);
//提供默认参数值,输出到屏幕上,你可以不提供第三个实参。
//...
private:
static const int _max_elems=1024;
static vector<int>_elems;
};//注意这个;别忘了。
- 当我们在类主体外进行成员函数的定义的时候,如果声明该成员函数
时最前头加了static,则定义时最前头不用再写static了,举个例子:
//最前头没有写static
void Triangular::gen_elems_to_value(int value)
{
//...
}
PS:书上这一节的例子我敲入计算机里,输出结果和书上的有出入,所以我未给出实战例子。望路过的小伙伴们指正,感谢!