C++基础(5)类与对象(二)
1. 静态成员
在类定义中,它的成员(包括成员变量和成员函数),这些成员可以用关键字static声明为静态的,称为静态成员。
不管这个类创建了多少个对象,静态成员只有一个拷贝,这个拷贝被所有属于这个类的对象共享。
静态成员变量在构造时跟其他非静态成员变量不在同一个内存空间。
1.1 静态成员变量
静态成员变量在构造时跟其他非静态成员变量不在同一个内存空间。
静态变量,是在编译阶段就分配空间,对象还没有创建时,就已经分配空间。
- 静态成员变量必须在类中声明,在类外定义。
- 静态数据成员不属于某个对象,在为对象分配空间中不包括静态成员所占空间。
- 静态数据成员可以通过类名或者对象名来引用。
#include<iostream>
using namespace std;
class A
{
public:
static int m_a;
};
int A::m_a =20;//必须先初始化,初始化时不加static
void main()
{
A a;
a.m_a = 20;
A b;
b.m_a = 10;
cout<<a.m_a<<endl;//共享数据,所以a的值也会改动
}
1.2 静态成员函数
前面有static说明的成员函数称为静态成员函数。静态成员函数使用方式和静态变量一样,同样在对象没有创建前,即可通过类名调用。静态成员函数主要为了访问静态变量,但是,不能访问普通成员变量。
作用:
管理静态数据成员,完成对静态数据成员的封装。
- 静态成员函数只能访问静态变量,不能访问普通成员变量
- 静态成员函数的使用和静态成员变量一样
- 静态成员函数也有访问权限
- 普通成员函数可访问静态成员变量、也可以访问非经常成员变量
class B
{
public:
static void u()
{
cout<<a<<endl;
//cout<<n<<endl;//或报错,只能访问静态成员变量
cout<<c<<endl;//c 不可修改,并且在内部初始化了
}
void v()
{
cout<<"非静态函数"<<endl;
cout<<a<<endl;//可以访问静态成员变量
cout<<n<<endl;//可以访问非静态成员变量
}
static int a;
string n;
static const int c =20;//如果一个类的成员,既要实现共享,又要实现不可改变,那就用 static const 修饰。定义静态const数据成员时,最好在类内部初始化。
};
int B::a = 10;
void test()
{
B b;
b.a = 10;
b.n = "aaaa";
b.u();
b.v();
}
void main()
{
test();
}
1.3 静态成员实现单例模式
单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
即:该类别只有一个实例化对象。
示例:用单例模式,模拟公司员工使用打印机场景,打印机可以打印员工要输出的内容,并且可以累积打印机使用次数。
#include<iostream>
#include<string>
using namespace std;
class printers
{
public:
static printers * myprint()//定义接口给外部可以访问到唯一的实例对象
{return myprinter;};
void useprint(string name)
{
cout<<name<<"使用了打印机"<<endl;
num++;
}
void get_num()
{
cout<<"打印机使用了 "<<num<<" 次"<<endl;
}
private:
printers()//将构造函数属性改为私有不可访问保证对象的唯一性
{
num =0;
}
printers(const printers&){} // 复制构造函数同样设为私有
int num;
static printers * myprinter;//定义静态变量为他本身
};
printers * printers::myprinter = new printers;//初始化
void test()
{
printers * myprint = printers::myprint();
myprint->useprint("张三");
myprint->useprint("法外狂徒张三");
myprint->useprint("三虽迟必到");
myprint->get_num();
}
void main()
{
test();
}
2. 类中的成员变量和函数的存储
c++实现了“封装”,那么数据(成员属性)和操作(成员函数)是什么样的呢?
“数据”和“处理数据的操作(函数)”是分开存储的。
- c++中的非静态数据成员直接内含在类对象中,就像c struct一样。
- 成员函数(member function)虽然内含在class声明之内,却不出现在对象中。
- 每一个非内联成员函数(non-inline member function)只会诞生一份函数实例.
#include<iostream>
#include<string>
using namespace std;
class MyClass01{
public:
int mA;
};//只有成员变量int类型 4个字节
class MyClass02{
public:
int mA;
static int sB;
};// 只有4个字节,静态变量不在类分配空间内,
class MyClass03{
public:
void printMyClass(){
cout << "hello world!" << endl;
}
public:
int mA;
static int sB;
};//函数和成员变量不在一个内存空间内所以也是4
class MyClass04{
public:
void printMyClass(){
cout << "hello world!" << endl;
}
static void ShowMyClass(){
cout << "hello world!" << endl;
}
public:
int mA;
static int sB;
};//静态成员函数也不保存在类对象中 也是 4
int main(){
MyClass01 mclass01;
MyClass02 mclass02;
MyClass03 mclass03;
MyClass04 mclass04;
cout << "MyClass01:" << sizeof(mclass01) << endl; //4
//静态数据成员并不保存在类对象中
cout << "MyClass02:" << sizeof(mclass02) << endl; //4
//非静态成员函数不保存在类对象中
cout << "MyClass03:" << sizeof(mclass03) << endl; //4
//静态成员函数也不保存在类对象中
cout << "MyClass04:" << sizeof(mclass04) << endl; //4
return EXIT_SUCCESS;
}
3. 友元函数
3.1 概述
类的主要特点之一是数据隐藏,即类的私有成员无法在类的外部(作用域之外)访问。但是,有时候需要在类的外部访问类的私有成员,怎么办?
解决方法是使用友元函数,友元函数是一种特权函数,c++允许这个特权函数访问私有成员。这一点从现实生活中也可以很好的理解:
比如你的家,有客厅,有你的卧室,那么你的客厅是Public的,所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去,但是呢,你也可以允许你的闺蜜好基友进去。
程序员可以把一个全局函数、某个类中的成员函数、甚至整个类声明为友元。
3.2 友元函数语法
- friend关键字只出现在声明处
- 其他类、类成员函数、全局函数都可声明为友元
- 友元函数不是类的成员,不带this指针
- 友元函数可访问对象任意成员属性,包括私有属性
#include<iostream>
#include<string>
using namespace std;
class Building;
//友元类
class MyFriend{
public:
//友元成员函数
void LookAtBedRoom(Building& building);
void PlayInBedRoom(Building& building);
};
class Building{
//全局函数做友元函数
friend void CleanBedRoom(Building& building);
#if 0
//成员函数做友元函数
friend void MyFriend::LookAtBedRoom(Building& building);
friend void MyFriend::PlayInBedRoom(Building& building);
#else
//友元类,友元类中函数可以访问类中变量
friend class MyFriend;
#endif
public:
Building();//构造函数
public:
string mSittingRoom;
private:
string mBedroom;
};
void MyFriend::LookAtBedRoom(Building& building){
cout << "我的朋友参观" << building.mBedroom << endl;
}
void MyFriend::PlayInBedRoom(Building& building){
cout << "我的朋友玩耍在" << building.mBedroom << endl;
}
//友元全局函数
void CleanBedRoom(Building& building){
cout << "友元全局函数访问" << building.mBedroom << endl;
}
Building::Building(){
this->mSittingRoom = "客厅";
this->mBedroom = "卧室";
}
int main(){
Building building;
MyFriend myfriend;
CleanBedRoom(building);//友元全局函数访问卧室
myfriend.LookAtBedRoom(building);//我的朋友参观卧室
myfriend.PlayInBedRoom(building);//我的朋友玩耍在卧室
system("pause");
return EXIT_SUCCESS;
}
补充说明:
- 友元关系不能被继承。
- 友元关系是单向的,类A是类B的朋友,但类B不一定是类A的朋友。
- 友元关系不具有传递性。类B是类A的朋友,类C是类B的朋友,但类C不一定是类A的朋友。