1.静态成员
- 可以定义类的静态成员, 能够实现同类的多个对象之间数据共享。
- 使用类的静态成员的优点是:
①静态成员的名字是在类的作用域中, 因此可以避免与其他类的成员或全局对象名字冲突;
②静态成员可以实施封装, 可以是私有成员, 而全局对象不可以。
③静态成员是与特定类关联的, 结构清晰。
- eg:
class employee { //雇员类定义
private:
int empNo;
int id;
char *name;
……
};
- 静态数据成员是类的一种特殊数据成员, 它以关键字static开始,声明形式为:
class 类名
{ //类体
…
static 数据成员类型 数据成员名列表; //静态数据成员声明
…
};
eg:count设计的目的是计数Data类总共有多少个实例化对象。
class Data//Data类定义
{
public:
static int count;//静态数据成员
int maxlevel;//非静态公有数据成员
Data(int i=0){...,count++;}//构造函数
private:
int level;//非静态私有数据成员
};
int Data::count=0;//静态数据成员定义且初始化
说明:
实例化对象,就会给每个对象分配存储空间,也就给该对象的每个数据成员分配存储空间
count属于类,不属于对象,一个类只有一个static数据成员
- 静态数据成员存储示意图
- 关于静态数据成员的说明:
(1) 通常, 非静态数据成员存在于类类型的每个对象中, 静态数据成员则独立于该类的任何对象, 在所有对象之外单独开辟空间存储。
在为对象所分配的空间中不包括静态数据成员所占的空间。
(2) 如果只声明了类而未定义对象, 则类的非静态数据成员是不占存储空间的, 只有在定义对象时, 才为对象的数据成员分配空间。
但是只要在类中定义了静态数据成员, 即使不定义任何对象, 也为静态数据成员分配空间, 它可以在尚未建立对象时就被引用。
(3) 访问静态成员时同样需要遵守公有及私有访问规则。
(4) 静态数据成员必须在类外部定义一次(仅有一次) , 静态成员不能通过类构造函数进行初始化, 而是在类外定义时进行初始化。
定义静态数据成员的方式为:
数据成员类型 类名::静态数据成员名=初始化式;
(5)静态数据成员可用作默认实参, 非静态数据成员不能用作默认实参, 因为它的值不能独立于所属的对象而使用。
例如:
class Data
{ //Data类定义
…
Data& setbkcolor(int=bkcolor);
static const int bkcolor = 5;
};
(6)有了静态数据成员(相当于全局变量), 各对象之间实现了数据共享, 因此可以不使用全局变量。
class test
{
private:
int x;
int y;
public:
static int num;
static int Getnum()
{
x+=5;
num+=15;
return num;
}
};
int test::num=10;
int main(void)
{
test a;
cout<<test::num<<endl; //输出10
test::num=20;
cout<<test::num<<endl; //输出20
cout<<test::Getnum()<<endl; //输出35
cout<<a.Getnum()<<endl;//输出50
return 0;
}
2.静态成员函数
- 成员函数也可以定义为静态的, 在类中声明函数的前面加static就成了静态成员函数, 声明的一般形式为:
class 类名
{ //类体
…
static 返回类型 函数名(类型1 参数名1,类型2 参数名2,…);
…
};
eg:
static int getcount()
{
return count;
} //静态成员函数
- 静态成员函数的两种调用方式
和静态数据成员一样, 静态成员函数是类的一部分, 而不是对象的一部分。
如果要在类外调用公有的静态成员函数, 可以类作用域运算符(::) 和通过对象名调用静态成员函数,
例如:
cout<<Data::getcount()<<'\t'<<d.getcount();
- 静态成员函数与非静态成员函数的根本区别是: 非静态成员函数有this指针, 而静态成员函数没有this指针。
因此, 静态成员函数不能访问本类中的非静态成员。 静态成员函数就是专门为了访问静态数据成员的。
- 静态成员函数不能被声明为const。
- eg:
using namespace std;
class CTest
{
public:
CTest()
{
s_total++;
id=s_total;
cout<<"构造"<<id<<" ";
}
~CTest()
{
s_total--;
cout<<"析构"<<id<<" ";
}
static int gettotal()
{
return s_total;//静态成员函数可以访问静态成员
}
private:
static int s_total;
int id;
};
int CTest::s_total=0;
int main(void)
{
CTest a,b,c;//调用三次了构造函数
CTest *p=new CTest;//调用一次了构造函数
cout<<"合计="<<CTest::gettotal()<<" ";//输出4
delete p;
cout<<"合计="<<CTest::gettotal()<<" ";//输出3
return 0;
}
- eg:编译出错,因为不能通过类名来调用类的非静态成员函数init。
class Test
{
void init(){}
static void output()
{}
};
int main(void)
{
Test::init();//错的
Test::output();//对的
return 0;
}
- eg:编译通过, 类的对象可以使用静态成员函数和非静态成员函数。
class Test
{
public:
void init() { }
static void output() { }
}
int main()
{
Test t;
t.init();
t.output();
return 0;
}
- eg:编译出错, 因为静态成员函数中不能引用非静态成员。
静态成员函数属于整个类, 在类实例化对象之前就已经分配空间了,而类的非静态成员必须在类实例化对象后才有内存空间
class Test
{
public:
void init() { }
static void output()
{
cout<<x<<endl;
}
private:
int x;
};
int main()
{
Test t;
t.output();
return 0;
}
- eg:编译通过, 因为类的非静态成员函数可以调用用静态成员函数, 但反之不能。
class Test
{
public:
void init()
{
output();
}
static void output()
{
}
};
int main()
{
Test t;
t.output();
return 0;
}
- eg:链接错误, 因为类的静态数据成员在使用前必须先初始化。
如果在main函数前加上int Point :: m = 0;
再编译链接无错误, 运行程序将输出1。
class Test
{
public:
Test()
{
m++;//正确
}
~Test()
{
m--;//正确
}
static void output()
{
cout<<m<<endl;
}
private:
static int m;
}
int main()
{
Test t;
t.output();
return 0;
}