类与对象(三)
💬 :如果你在阅读过程中有任何疑问或想要进一步探讨的内容,欢迎在评论区畅所欲言!我们一起学习、共同成长~!
👍 :如果你觉得这篇文章还不错,不妨顺手点个赞、加入收藏,并分享给更多的朋友噢~!
1. static 成员
1.1 概念
C++ 里,声明为
static
的类成员被称作类的静态成员。用
static
修饰的成员变量是静态成员变量。特别注意:静态成员变量一定要在类外进行初始化。
用
static
修饰的成员函数是静态成员函数。
实现一个类,用于计算程序中创建出的类对象的数量:
#include <iostream>
using namespace std;
class A
{
public:
A() { ++_scount; }
A(const A& t) { ++_scount; }
~A()
{
--_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()
{
TestA();
return 0;
}
1.2 特性
- 共享性:静态成员为所有类对象所共享,并不属于某个具体的对象,它存放在静态区。
- 定义静态成员变量:静态成员变量必须在类外进行定义,并且在定义时不需要添加
static
关键字,类中仅仅是声明。 - 访问方式:类静态成员既能使用
类名::静态成员
的方式访问,也可用对象.静态成员
的方式访问。 - 静态成员函数:静态成员函数没有隐藏的
this
指针,所以不能访问任何非静态成员。而非静态成员函数可以访问类的静态成员。 - 访问限定:静态成员同样是类的成员,会受到
public
、protected
、private
访问限定符的限制。
2. 友元
友元是 C++ 中一种突破封装的机制(破坏封装性,所以谨慎使用)。
友元主要分为友元函数和友元类。
2.1 友元函数
在 C++ 中重载 operator<<
和 operator>>
时,无法将其重载为成员函数。因为成员函数有隐含的 this
指针作为首个参数,若重载为成员函数,调用形式会变成 对象 << cout
或 对象 >> cin
,与常用的 cout << 对象
、cin >> 对象
不符。所以需将它们重载为全局函数,让 cout
或 cin
作为首个形参。
但全局函数在类外,无法直接访问类的私有成员,而实现 operator<<
和 operator>>
又常需访问这些私有成员。
为解决该矛盾,C++ 引入友元机制。在类中声明 operator<<
和 operator>>
为友元函数,可使全局函数访问私有成员,从而在符合使用习惯的同时,实现输入输出流运算符的重载。
class ClassName
{
// 其他成员声明
friend 返回类型 函数名(参数列表);
// 其他成员声明
};
#include <iostream>
using namespace std;
class Date
{
friend ostream& operator<<(ostream& _cout, const Date& d);
// ostream&是输出流对象的引用,实现链式输出
friend istream& operator>>(istream& _cin, Date& d);
// istream&是输入流对象的引用,实现链式输入
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{
_cin >> d._year;
_cin >> d._month;
_cin >> d._day;
return _cin;
}
int main()
{
Date d;
cin >> d;
cout << d << endl;
return 0;
}
友元函数特性
- 访问权限:友元函数可以访问类的 private 和 protected 成员,但它本身并不是类的成员函数。
const
修饰:友元函数不能使用const
进行修饰。- 声明位置:友元函数可以在类定义的任意位置声明,不受类访问限定符的限制。
- 多类友元:一个函数可以成为多个类的友元函数。
- 调用原理:友元函数的调用原理与普通函数相同。
2.2 友元类
友元类的所有成员函数都能够成为另一个类的友元函数,从而可以访问该非友元类中的非公有成员。
#include <iostream>
using namespace std;
class Time
{
friend class Date;
// 声明Date类为Time类的友元类,则在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)
{
// 直接访问Time类私有成员变量
_t._hour = hour;
_t._minute = minute;
_t._second = second;
}
void Print()
{
cout << "Date: " << _year << "-" << _month << "-" << _day << endl;
cout << "Time: " << _t._hour << ":" << _t._minute << ":" << _t._second << endl;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
int main()
{
Date date(2025, 3, 23);
date.SetTimeOfDate(12, 30, 0);
date.Print();
return 0;
}
Date
类内部创建了一个 Time
类的对象 _t,又 Date
类已被声明为 Time
类的友元类,那么在 Date 类中可直接访问 Time 类私有成员变量 _hour
、_minute
、_second 。
友元关系
- 单向性:友元关系不具有交换性。例如,若
Time
类声明Date
类为其友元类,那么在Date
类中可以直接访问Time
类的私有成员变量,但在Time
类中不能访问Date
类的私有成员变量。 - 不可传递:如果
B
是A
的友元,C
是B
的友元,并不能说明C
是A
的友元。 - 不可继承:友元关系不能继承,在后续的继承部分会详细介绍。
3. 内部类
当一个类的定义嵌套于另一个类的内部时,处于内部的这个类被称作内部类。
内部类特性
- 内部类是一个完全独立的类,它并不归属于外部类。
- 外部类的对象无法直接访问内部类的成员。
- 在访问内部类成员时,外部类遵循与其他普通类相同的访问规则,受到内部类自身访问限定符(如 public、protected、private)的约束,没有额外的特权可以绕过这些访问限制。
- 内部类天生是其外部类的友元类。内部类可访问外部类的所有成员。
- 定义位置:内部类可定义在外部类的
public /
protected /
private
区域。 - 静态成员访问:内部类可直接访问外部类中的
static
成员,无需借助外部类的对象或类名。 - 创建内部类对象:外部类 A,内部类 B,通过“ A::B 对象名; ”创建内部类对象。
- 大小无关性:
sizeof(外部类)
的结果与内部类没有任何关系。
#include <iostream>
using namespace std;
class A
{
private:
static int k;
int h;
public:
class B
{
public:
void foo(const A& a) // 常量引用类型的 A 类对象作参数
{
// 访问外部类 A 的私有成员
cout << k << endl;
cout << a.h << endl;
}
};
};
int A::k = 1;
int main()
{
A::B b; // 创建内部类 B 的对象 b
b.foo(A()); // 传入一个临时的 A 类对象
return 0;
}
4. 再次理解类和对象
计算机仅能识别二进制数据,无法直接认识现实生活中的实体。
借助面向对象编程思想,可按以下步骤让计算机认识实体:
- (1)实体抽象:用户从人为思想层面剖析现实实体的属性与功能。如明确洗衣机的容量、功率等属性,以及洗涤、脱水等功能。
- (2)类的描述:人对实体有清晰认知后,通过 C++、Java、Python 等面向对象语言,将实体特征用类描述并输入计算机,定义类的成员变量(描述属性)和成员函数(实现功能)。
- (3)对象实例化:计算机有了类的定义后,通过实例化创建具体对象。对象是类的具体呈现,拥有类的属性和方法,占据独立内存空间,此时计算机才真正认识实体。
- (4)模拟现实:用户借助实例化的对象,调用其功能模拟现实实体行为,如操作洗衣机对象进行洗涤。
学习类和对象时,要明白类是对一类实体(对象)的抽象描述,定义属性和方法后形成新的自定义类型,利用该类型可实例化出具体对象,为计算机模拟现实提供工具。