在前面1-5篇中详细讲解一些类的示例,其中每个对象都有自己的数据成员数据集。 该类的每个成员函数都可以访问其类的任何对象的任何成员。在某些情况下,可能希望定义可以由该类的所有实例都能访问的公共数据字段。在C中也遇到这种情况,C源文件中的几个函数需要访问同一变量。 C的一种常见解决方案是同个源文件中定义所有函数,并且定义一个static变量.在源文件之外,变量名不可见。 这种方法非常有效,但是违反了我们在每个源文件中仅使用一个函数的原则。
C ++通过定义静态成员来解决该问题:这些静态成员是类的所有对象所共有的数据和函数,并且(在私有部分中定义时)在类外部无法访问。 这些静态成员是本章的主题。
静态成员不能定义为virtual函数。 虚拟成员函数是普通成员,因为它具有this指针。 由于静态成员函数没有此指针,因此无法将其声明为virtual。
静态数据
类的任何数据成员都可以声明为static; 无论是在类接口的public部分还是private部分中。 与为该类的每个对象一次又一次创建的非静态数据成员相反,此类数据成员仅创建和初始化一次。
程序启动后立即创建静态数据成员。 即使它们是在程序执行周期的最开始创建的,它们仍然是其类的真正成员。为了方便区分静态数据成员和普通数据成员建议为静态数据成员的名称加上s_前缀。
公共静态数据成员是全局变量。 程序的所有代码都可以访问它们,只需使用其类名,范围解析运算符及其成员名即可。以下是详细的例子:
类接口:cone.hh文件
#include <string>
class Cone
{
std::string d_name;
double d_radius;
double d_height;
double d_busLength;
public:
static double s_pi;
static int s_count;
Cone(std::string, double, double);
//计算体积
double volume();
//计算底面面积
double bottom_area();
//计算表面积
double surface_area();
//计算侧面积
double side_area();
//显示容积
void display_volume();
};
如果没有其他初始化,则在创建第一个对象时,所有静态数据都会根据其声明的数据类型进行类内默认值初始化。
静态数据成员的初始化:不能将其放在类接口中,一般来说,公共的静态数据成员是放在类接口外部进行初始化,方法是使用作用域运算符::来重新声明静态变量,以标识它属于哪个类,下面是一个很贴切的例子。
类实现:cone.cpp文件
#include "../header/cone.hh"
#include <iostream>
#include <math.h>
// 静态成员在类接口外初始化
int Cone::s_count = 0;
double Cone::s_pi = 3.14;
Cone::Cone(std::string name, double r, double h) : d_radius(r), d_height(h), d_name(name)
{
s_count++;
std::cout << "创建一个锥体,目前数量是" << s_count << std::endl;
if (d_radius > 0 && d_height > 0)
{
d_busLength = sqrt(d_radius * d_radius + d_height * d_height);
}
}
inline double Cone::volume()
{
return s_pi * d_radius * d_radius * d_height / 1000 / 3.0;
}
inline void Cone::display_volume()
{
std::cout << "锥体"<<d_name<<"的容积是" << volume() << "L" << std::endl;
}
double Cone::side_area()
{
//公式PI*R*L
return s_pi * d_radius * d_busLength;
}
inline double Cone::surface_area()
{
return side_area() + bottom_area();
}
inline double Cone::bottom_area()
{
//公式:PI*R*R
return s_pi * d_radius * d_radius;
}
调用代码:
int main(int argc, char const *argv[])
{
Cone c1 = {"N1", 43.5, 72.5};
Cone c2 = {"N2", 52.77, 23.33};
Cone c3 = {"N3", 47.5, 72.5};
Cone c4 = {"N4", 32.77, 33.33};
c1.display_volume();
c2.display_volume();
c3.display_volume();
c4.display_volume();
return 0;
}
而实际项目中,我们更倾向于将整个项目的所有类的静态数据成员集中在某个源文件当中(例如:st_data.cpp),然后在程序入口的源文件include这个st_data.cpp就可以了。
由于构造函数是为类的每个新对象调用的,因此构造函数不会初始化静态数据成员。 最多就是修改。 原因是静态数据成员在调用该类的任何构造函数之前就已经存在。 定义静态数据成员时,在任何成员函数之外都将对其进行初始化,就像对普通(非类)全局变量的初始化一样。
静态成员函数
除了静态数据成员,C ++还允许我们定义静态成员函数。与类的所有对象共享的静态数据类似,静态成员函数的特征如下。
-
静态成员函数可以访问其类的所有静态成员,也可以访问其类的对象的成员(包括private或public),但
前提要指定一个该类的对象
. - 静态成员函数并没与其类的任何对象关联,因此它们没有this指针。
- 静态成员函数只能从类外部访问静态数据成员,其他静态成员函数和任何其他函数。
- 静态成员函数可以被在类接口的public部分声明的其他静态成员函数调用,而无需指定其类的对象。以下示例说明了静态成员函数的这种特征:
下面的示例是为了上面的静态成员函数的特征的,我们在前文的Cone类接口中增加两个静态成员函数的声明
类接口
class Cone{
.....
//显示侧面面积
static void display_sideArea(Cone &);
//显示表面积
static void display_surfArea(Cone &);
}
类实现
很多情况下:我们可能需要用静态成员函数,调用它所属的类的某个实例的非静态数据成员 , 正是特征1所说的那样。特征2和3更不用说了,下面这些示例都是从类外调用的
void Cone::display_sideArea(Cone &obj)
{
std::cout << obj.d_name << "侧面积:" << obj.side_area() << std::endl;
}
void Cone::display_surfArea(Cone &obj)
{
std::cout << obj.d_name << "表面积:" << obj.surface_area() << std::endl;
}
下面是静态成员函数调用同一个类的静态函数,正如上面特征4所述
void Cone::display_all(Cone &obj)
{
std::cout << "型号:" << obj.d_name << std::endl;
std::cout << "参数:"
<< "r " << obj.d_radius << " h " << obj.d_height << " l " << obj.d_busLength << std::endl;
display_surfArea(obj);
display_sideArea(obj);
}
调用代码
#include "./cone.cpp"
#include <iostream>
using namespace std;
int main(int argc, char const *argv[])
{
Cone c1 = {"N1", 43.5, 72.5};
Cone c2 = {"N2", 52.77, 23.33};
Cone c3 = {"N3", 47.5, 72.5};
Cone c4 = {"N4", 32.77, 33.33};
c1.display_volume();
c2.display_volume();
c3.display_volume();
c4.display_volume();
Cone::display_sideArea(c1);
Cone::display_surfArea(c2);
Cone::display_all(c1);
Cone::display_all(c2);
return 0;
}
网友评论