声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化。
静态成员变量: 静态成员变量是类的所有实例共享的变量。它们存储在静态数据区,并在程序运行期间只有一个副本。静态成员变量可以通过类名访问,而不需要类的实例对象。
静态成员函数: 静态成员函数是不依赖于类的实例的函数。它们可以通过类名直接调用,而不需要创建类的实例对象。静态成员函数只能访问静态成员变量和静态成员函数,不能访问非静态成员变量和非静态成员函数。
例如: 面试题:实现一个类,计算程序中正在使用的类对象有多少个。
我们如果使用普通的成员变量来统计,在每次创建对象时就创建一个属于该对象的变量,每次该变量都会被初始化,无法计算,所以我们就可以通过静态成员变量来统计,它是类的所有实例共享的变量,并不会每个对象都创建一个,所以可以用来统计数据。
#include<iostream>
using namespace std;
class A
{
public:
A() //构造函数
{
++_scount;
}
A(const A& t) //拷贝构造函数
{
++_scount;
}
~A() //析构函数
{
--_scount;//类对象生命周期结束调用析构函数,所以正在使用的对象数量_scount--
}
static int GetACount() //静态成员函数
{
return _scount;
}
private:
static int _scount; //静态成员变量
};
// 静态成员变量的初始化
int A::_scount = 0;
void TestA()
{
cout << A::GetACount() << endl;
A a1, a2;
A a3(a1);
cout << A::GetACount() << endl;
}
int main()
{
//A aa;
//cout << sizeof(A) << endl;
TestA();
return 0;
}
这里注意静态成员初始化要在类外面; 计算正在使用的类对象个数可以通过计算一共构建了多少个类对象减去生命周期已经结束的类对象; 而构建类对象无非通过普通的构造函数或者拷贝构造来实现,所以我们在这两个函数内++_scount,一旦构造函数被调用就说明创建了一个新的对象,_scount就+1; 类对象生命周期结束调用析构函数,所以相应的_scount-1。
结果如下:
此外我们还可以通过上述例子创建一个类分别实现计算程序中总共创建了多少个类对象以及正在使用的类对象有多少个: 代码如下:
#include<iostream>
using namespace std;
class A
{
public:
A() //构造函数
{
++m;
++n;
}
A(const A& t) //拷贝构造函数
{
++m;
++n;
}
~A() //析构函数
{
--m;
}
static void GetCount()
{
cout << m << " " << n << endl;
}
private:
static int m; //静态成员变量,计算正在使用的对象个数
static int n; //静态成员变量,计算一共创建了多少个对象
};
int A::m = 0;
int A::n = 0;
A FuncA(A aa)
{
return aa;//返回也会创建临时变量,拷贝构造
}
void TestA()
{
A a1;
A a2(a1);//a2用a1来拷贝构造
A::GetCount();
FuncA(a1);//函数传参会拷贝构造
A::GetCount();
}
int main()
{
//A aa;
//cout << sizeof(A) << endl;
TestA();
return 0;
}
例如计算以下类的大小:
class A
{
public:
A()
{
++_scount;
}
A(const A& t)
{
++_scount;
}
~A()
{
--_scount;
}
static int GetACount()
{ return _scount;
}
private:
static int _scount;
};
类的大小计算是通过它的成员变量来计算的,而此类的大小是1,说明该类的静态成员变量存储在静态数据区,并在程序运行期间只有一个副本,并不构成该类的大小;
例如:
class A
{
public:
A() //构造函数
{
++m;
++n;
}
A(const A& t) //拷贝构造函数
{
++m;
++n;
}
~A() //析构函数
{
--m;
}
static void GetCount()
{
cout << m << " " << n << endl;
}
static int m; //静态成员变量,计算正在使用的对象个数
static int n; //静态成员变量,计算一共创建了多少个对象
};
int A::m = 0;
int A::n = 0;
void TestA()
{
cout << A::m << endl;//通过类名直接访问静态成员变量
A a1;
cout << a1.n << endl;//通过对象. 来访问静态成员变量
}
int main()
{
TestA();
return 0;
}
如上图所示,类中静态成员函数不能访问非静态成员变量a;
静态成员变量是属于类而不是类的实例的成员变量。在C++中,静态成员变量被所有类的对象所共享,而不是每个对象单独拥有一份副本。以下是一些使用静态成员变量的原因:
需要注意的是,静态成员变量的生命周期与程序的运行周期相同,它们在程序启动时创建,直到程序结束才被销毁。因此,在使用静态成员变量时需要注意其初始化和使用的时机。 此外,静态成员函数一般与静态成员变量搭配使用,因为静态成员函数没有隐藏的this指针,不能访问非静态成员变量;
静态成员函数可以调用非静态成员函数。静态成员函数在不涉及对象的特定实例时被调用,而非静态成员函数是与对象的特定实例相关的。在静态成员函数中调用非静态成员函数时,需要提供一个对象来调用该非静态成员函数。
非静态成员函数可以调用类的静态成员函数。静态成员函数是与类本身相关而不是与对象的特定实例相关的,因此可以在非静态成员函数中直接使用类名来调用静态成员函数。
友元(friend)是C++中的一个特殊关键字,用于在类中声明其他类或函数为友元。友元类或函数可以访问声明它为友元的类的私有成员和保护成员。
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
友元分为:友元函数和友元类
在类中声明一个函数为友元函数。该函数可以访问声明它为友元的类的私有成员和保护成员。
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
class A {
friend void friendFunc(); // 声明friendFunc为友元函数
};
void friendFunc() {
A obj;
// 可以访问类A的私有成员和保护成员
}
例如之前写过的流输入输出重载就使用了友元函数,以此来访问类的私有成员;
注意:
在类中声明另一个类为友元类。友元类可以访问声明它为友元的类的所有私有成员和保护成员。
友元类声明时加friend class [类名]
class A {
friend class B; // 声明类B为友元类
};
class B {
public:
void func() {
A obj;
// 可以访问类A的私有成员和保护成员
}
};
例如:
class Time
{
friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量
public:
Time(int hour = 0, int minute = 0, int second = 0)
: _hour(hour)
, _minute(minute)
, _second(second)
{}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
void SetTimeOfDate(int hour, int minute, int second)
{
// 直接访问时间类私有的成员变量
_t._hour = hour;
_t._minute = minute;
_t._second = second;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
上述例子声明日期类Date是时间类Time的友元类,在日期类中就可以直接访问Time类的私有成员变量;
注意:
内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。 注意:内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。
例如:
class A
{
private:
static int k;//静态成员变量
int h;
public:
class B // B天生就是A的友元
{
public:
void foo(const A& a)
{
cout << k << endl;//OK,直接访问A的私有成员
cout << a.h << endl;//OK,通过a.间接访问A的私有成员
}
};
};
int A::k = 1;//静态变量初始化在类外部
int main()
{
A::B b;
b.foo(A());
return 0;
}
结果如下:
现实生活中的实体计算机并不认识,计算机只认识二进制格式的数据。如果想要让计算机认识现实生活中的实体,用户必须通过某种面向对象的语言,对实体进行描述,然后通过编写程序,创建对象后计算机才可以认识。
比如想要让计算机认识洗衣机,就需要:
在类和对象阶段,类是对某一类实体(对象)来进行描述的,描述该对象具有哪些属性,哪些方法,描述完成后就形成了一种新的自定义类型,用该自定义类型就可以实例化具体的对象。
✨static静态成员在一些场景下可以用来保存全局数据或提供公共的工具函数,也可以用来实现单例模式等。但需要注意,在多线程环境下操作静态成员时需要考虑线程安全性。 ✨友元关系提供了一种在需要的情况下访问其他类的私有成员的机制。但由于友元关系破坏了封装性,一般不使用。 ✨内部类是指在一个类的内部定义的另一个类。内部类可以直接访问外部类的成员,包括私有成员,而外部类则不能直接访问内部类的成员。 此外我们还再次理解了类和对象的含义,以上就是今天所有的内容啦~ 完结撒花 ~🥳🎉🎉