static成员
- 对特定类型的全体对象而言,有时候需要访问一个全局的变量。比如说统计某种类型对象已创建的数量;
- 如果我们用全局变量会破坏数据的封装,一般的用户代码都可以修改这个全局变量,这是我们可以用类的静态成员来解决这个问题;
- 非static数据成员存在于类类型的每个对象中,static数据成员独立该类的任意对象存在,它是与类关联的对象,不与类对象关联。
static成员声明、定义与初始化
- static成员需要在类定义体外进行初始化与定义
/*---------------------示例代码------------------------*/
//CountedObject.h
#ifndef _COUNTED_OBJECT_H_
#define _COUNTED_OBJECT_H_
class CountedObject
{
public:
CountedObject();
~CountedObject();
static int count_; //静态成员的引用性声明
};
#endif //_COUNTED_OBJECT_H_
//CountedObject.cpp
#include "CountedObject.h"
int CountedObject::count_ = 0; //静态成员的定义性声明,此时不需要指定static
//静态成员的初始化也必须放在文件作用域
CountedObject::CountedObject()
{
++count_;
}
CountedObject::~CountedObject()
{
--count_;
}
//01.cpp
#include "CountedObject.h"
#include<iostream>
using namespace std;
int main(void)
{
CountedObject ob1;
cout << CountedObject::count_ <<endl;
CountedObject* ob2 = new CountedObject;
cout << CountedObject::count_ <<endl;
delete ob2;
cout << CountedObject::count_ <<endl;
return 0;
}
特殊的整型static const成员:整型static const成员可以在类定义体中初始化
#include<iostream>
using namespace std;
class Test
{
public:
Test()
{}
~Test()
{}
static const int x_ = 100; //静态常量整型成员的初始化可以直接在类体内进行
};
int main(void)
{
cout<<Test::x_<<endl;
}
static成员优点
- static成员的名字是在类的作用域中,因此可以避免与其它类成员或全局对象名字冲突
- 可以实施封装,static成员可以是私有的,而全局对象不可以
- 阅读程序容易看出static成员与某个类相关联,这种可见性可以清晰地反映程序员的意图
当静态成员声明为私有时,需要提供接口函数
#ifndef _COUNTED_OBJECT_H_
#define _COUNTED_OBJECT_H_
class CountedObject
{
public:
CountedObject();
~CountedObject();
public:
static int GetCounnt();//为私有的静态成员提供接口函数
private:
static int count_; //静态成员的引用性声明
};
#endif //_COUNTED_OBJECT_H_
int CountedObject::GetCounnt() //接口函数
{
return count_;
}
static成员函数
- static成员函数没有this指针
- static成员函数不可以访问非静态成员和非静态成员函数
- 非静态成员函数可以访问静态成员
class Test
{
public:
Test(int y) : y_(y)
{
}
~Test()
{
}
void TestFun()
{
cout<<"x="<<x_<<endl; //OK,非静态成员函数可以访问静态成员
TestStaticFun();
}
static void TestStaticFun()
{
cout<<"TestStaticFun ..."<<endl;
//TestFun(); Error,静态成员函数不能调用非静态成员函数
//cout<<"y="<<y_<<endl; Error,静态成员函数不能访问非静态成员
}
static int x_; // 静态成员的引用性说明
int y_;
};
类/对象大小计算
- 类大小计算遵循前面学过的结构体对齐原则
- 类的大小与数据成员有关与成员函数无关
- 类的大小与静态数据成员无关
- 虚函数对类的大小的影响
- 虚继承对类的大小的影响
四种对象的作用域与生存期
栈对象
隐含调用构造函数(程序中没有显示调用)
堆对象
隐含调用构造函数(程序中没有显示调用)
全局对象、静态全局对象
- 全局对象的构造先于main函数
- 已初始化的全局变量或静态全局对象存储于.data段中
- 未初始化的全局变量或静态全局对象存储于.bss段中
静态局部对象
- 已初始化的静态局部变量存储于.data段中
- 未初始化的静态局部变量存储于.bss段中
static用法总结
C语言中的用法
- 用于函数内部修饰变量,即函数内的静态变量。这种变量的生存期长于该函数,使得函数具有一定的“状态”。使用静态变量的函数一般是不可重入的,也不是线程安全的,比如strtok(3);
- 用在文件级别(函数体之外),修饰变量或函数,表示该变量或函数只在本文件可见,其他文件看不到也访问不到该变量或函数。专业的说法叫“具有internal linkage”(简言之:不暴露给别的translation unit)。
C++中添加的用法
- 用于修饰类的数据成员,即所谓“静态成员”。这种数据成员的生存期大于class的对象(实例/instance)。静态数据成员是每个class有一份,普通数据成员是每个instance 有一份;
- 用于修饰class的成员函数,即所谓“静态成员函数”。这种成员函数只能访问静态成员和其他静态程员函数,不能访问非静态成员和非静态成员函数。
static与单例模式
- 保证一个类只有一个实例,并提供一个全局访问点
- 禁止拷贝
#include<iostream>
using namespace std;
class Singleton
{
public:
static Singleton* GetInstance() //2. 提供一个全局访问点
{
if(instance_ == NULL)
{
instance_ = new Singleton; //构造
}
return instance_;
}
~Singleton()
{
cout << "~Singleton ..."<<endl;
}
private:
Singleton() //1. 将构造函数声明为私有的
{
cout<< "Singleton ..."<<endl;
}
Singleton(const Singleton& other); //
Singleton& operator=(const Singleton& other); //3. 将拷贝构造函数与等号运算符都声明为私有的,斌企鹅不提供其实现方式,那么就可以禁止拷贝了
static Singleton* instance_;
};
Singleton* Singleton::instance_;
int main(void)
{
Singleton* s1 = Singleton::GetInstance();//
Singleton* s2 = Singleton::GetInstance();// 二者调用的是同一个实例
return 0;
}
需要注意的是以上程序运行时系统是不会调用析构函数的,所以需要进一步改造
- 方案一:
#include<iostream>
using namespace std;
class Singleton
{
public:
static Singleton* GetInstance()
{
if(instance_ == NULL)
{
instance_ = new Singleton; //
}
return instance_;
}
~Singleton()
{
cout << "~Singleton ..."<<endl;
}
class Garbo //1. 提供一个嵌套类,在该类中进行释放
{
public:
~Garbo()
{
if(Singleton::instance_ != NULL)
{
delete Singleton::instance_;
}
}
};
private:
Singleton()
{
cout<< "Singleton ..."<<endl;
}
Singleton(const Singleton& other);
Singleton& operator=(const Singleton& other);
static Singleton* instance_;
static Garbo garbo_; //2. 当该对象生存周期结束时自动调用析构函数,从而达到
};
Singleton::Garbo Singleton::garbo_; // 3. 此处必须声明
Singleton* Singleton::instance_;
int main(void)
{
Singleton* s1 = Singleton::GetInstance();
Singleton* s2 = Singleton::GetInstance();
return 0;
}
- 方案二(推荐):
#include<iostream>
using namespace std;
class Singleton
{
public:
static Singleton& GetInstance() //全局访问点
{
static Singleton instance; //局部静态成员
return instance;
}
~Singleton()
{
cout << "~Singleton ..."<<endl;
}
private:
Singleton()
{
cout<< "Singleton ..."<<endl;
}
Singleton(const Singleton& other);
Singleton& operator=(const Singleton& other);
};
int main(void)
{
Singleton& s1 = Singleton::GetInstance();
Singleton& s2 = Singleton::GetInstance();
return 0;
}
const成员函数
- const成员函数不会修改对象的状态
- const成员函数只能访问数据成员的值,而不能修改它
class Test
{
public:
Test(int x):x_(x)
{}
int GetX() const
{
return x_;
}
private:
int x_;
};
const对象
- 如果把一个对象指定为const,就是告诉编译器不要修改它;
- const对象的定义:
const 类名 对象名(参数表) - const对象不能调用非const成员函数
int main(void)
{
const Test t(10); //const对象,对象的状态不能被更改,不能调用非const成员函数
cout << t.GetX() << endl; //GetX()为const成员函数,因此t可以调用
return 0;
}
mutable
- 用mutable修饰的数据成员即使在const对象或在const成员函数中都可以被修改
class Test
{
public:
Test(int x):x_(x),outputtimes_(0)
{}
int GetX() const
{
return x_;
}
void OutPutX() const
{
cout << "x = "<<x_<<endl;
outputtimes_++; //outputtimes_已被声明为mutable,此处可以修改它的值
}
int GetOutPutTimes() const
{
return outputtimes_;
}
private:
int x_;
mutable int outputtimes_;
};