1.静态成员变量
**静态成员变量必须在类中声明,在类外定义**
- 在一个类中,若将一个成员变量声明为
static
,这种成员称为静态成员变量. - 静态变量,是在编译阶段就分配了空间->对象还没有创建时,就已经分配了空间.
- 不管这个类创建了多少个对象,静态成员只有一个拷贝,这个拷贝被所有属于这个类的对象共享。
(1)public下
- 通过对象访问属性
不允许在类内初始化; 要在类外初始化,且要声明; static变量的值是最后修改的那一份!
class Person{
public:
static int Age;//[不允许在类内初始化]
};
int Person::Age=22;//[类外初始化]
void test01(){
Person p1;
p1.Age=1; //[最后打印p2.age]
Person p2;
p2.Age=10; //[最后打印p2.age]
cout<<"p1的年龄是"<<p1.Age<<endl<<"p2的年龄是"<<p2.Age<<endl;
}
- 通过类名访问static属性(一般采用这种方法)
cout<<"static变量age的值是"<<Person::Age<<endl;
(2)private下
- 定义时:
private:
static int weight;//私有权限->在类外不能访问
- 类外初始化时:
int Person::weight=90; //类外初始化能成功.仅限这里能成功.
- 调用时:
- 调用对象的get方法:
cout<<p1.getWeight()<<endl;
这样能成功! - 通过类名访问:
cout<<Person::weigth<<endl;
这样会失败!
- 调用对象的get方法:
2.静态成员函数
静态成员函数主要为了访问静态变量,但是,[不能访问普通成员变量!]。
静态成员函数的意义,不在于信息共享/数据沟通,而在于管理静态数据成员,完成对静态数据成员的封装。
普通成员函数可访问静态成员变量、也可以访问非经常成员变量.
- 静态成员函数的写法
static void func(int iAge){
Age=iAge;
cout<<"func调用了"<<endl;
}
- 静态成员函数的调用
p2.func(40);
p1.func(55); //通过对象调用
Person::func(60); //通过类名调用
3.静态成员实现单例模式
单例模式:在系统中某个类的对象只能存在一个
(1)概述
- 它提供一个静态static的getInstance() 工厂方法,让客户可以访问它的唯一实例.
- 为了防止在外部对其实例化,将其默认构造函数和拷贝构造函数设计为私有private.
- 在单例类内部定义了一个Singleton类型的静态static对象,作为外部共享的唯一实例.
(2)案例
①主席案例
- 类的写法:
- 只有getInstance()方法是public的
- 静态化这唯一的singleMan对象,在类内声明,类外初始化!
- 构造函数和拷贝构造函数都要私有化!
class ChairMan{
public:
static ChairMan* getInstance(){//通过get方法来访问private变量singleMan
return singleMan;
}
private:
static ChairMan * singleMan;//维护这唯一对象的指针->类内声明!
ChairMan(){ //构造函数私有化 ;
cout<<"创建国家主席"<<endl;
}
ChairMan(const ChairMan * cm){
//拷贝构造函数私有化;
}
};
- 类外初始化singleMan对象
ChairMan * ChairMan::singleMan=new ChairMan;//类外初始化!
- 调用的写法:
ChairMan * cm1=ChairMan::getInstance();
②打印机案例
题目:用单例模式模拟公司员工使用打印机场景,打印机可以打印员工要输出的内容,并且可以累积打印机使用次数。
- 类内的写法:
class Printer{
public:
static Printer * getInstance(){
return SinglePrinter;
}
void printText(string text){
cout<<"打印机打印了"<<text<<endl;
count++;
cout<<count<<endl;
}
private:
int count=0;
static Printer * SinglePrinter ;
Printer(){
cout<<"使用了打印机哟"<<endl;
}
Printer(const Printer * iprinter){
cout<<"使用了拷贝构造函数的打印机"<<endl;
}
};
- 类外的写法:
Printer * Printer::SinglePrinter=new Printer;
- 调用的写法:
void test(){
Printer * p1=Printer::getInstance();
p1->printText("一张A4纸");
Printer * p2=Printer::getInstance();
p2->printText("两份试卷");
}
4. 成员属性和函数的存储
c++实现了封装,那么成员属性和成员函数是如何存储的呢?
答:成员属性和成员函数是[分开存储]的.
- c++中的非静态数据属性直接内含在对象中.
- 静态成员属性,不在对象上.
- 成员函数(静态和非静态都)内含在class声明之内,但不出现在对象中.每一个非内联成员函数(non-inline member function)只会诞生一份函数实例.
5. this指针
上节提到的成员函数只会诞生一份实例,那不同对象调用同一个函数时是怎么区分的呢?
答:使用this指针,指向被调用的成员函数所属的对象.
- 所有的非静态成员函数
void func(Person * this)
,其中Person * this
是编译器默认添加的指针,我们看不到. - this永远指向当前对象!
- this用于解决命名冲突(形参和成员属性同名)
- 在类的非静态成员函数中返回对象本身,使用
return *this
6. 链式编程
Person& plus(Person & p){
this->age+=p.age;
return *this;//*this指向本体
}
p2.plus(p1).plus(p1).plus(p1).plus(p1)...;
- 函数返回Person&!
- return *this!
- 无限调用
7. 空指针访问成员函数
若Person *p=NULL;*
,则p可以调用[未使用到this]的成员函数.
8.常函数与常对象
利用const修饰成员函数
(1)const修饰成员函数->常函数
- 写法:
void func() const{}
void showPerson() const{
this->a=11;//这样就会报错了,因为用const修饰后不允许再修改成员属性
}
- 常函数的原理:修饰的是this指针->const Type * const this;
- 有一些比较特殊的变量希望能够在常函数中进行修改:则加上
mutable
关键字
mutable int b;
(2)const修饰对象->常对象
- 写法:
const ClassName className;
- 常对象初始化了后不允许修改属性:
const Person p2;
p2.a=11;//会报错,因为常对象不允许修改属性!
- 常对象不可以调用普通成员函数:
![[Pasted image 20221006144049.png]] - 常对象可以调用常函数.
9.友元friend
类的私有属性一般无法在作用域之外访问,但有时候需要在类的外部访问类的私有属性,怎么做到呢?
答:使用友元函数.
友元函数是一种特权函数,c++允许这个特权函数访问私有成员。
程序员可以把一个全局函数、成员函数、甚至整个类声明为友元。
①全局函数作友元函数
- 类内声明友元函数:
//让全局中的好基友函数变成我的好朋友->使用友元函数
friend void goodGay(Building * building);
- 全局中定义函数方法:
void goodGay(Building * building){//全局的好基友函数
cout<<"你的朋友在访问:"<<building->SittingRoom<<endl;
cout<<"你的朋友在访问:"<<building->BedRoom<<endl;//这个BedRoom是private变量
}
- 调用写法:
goodGay(&building1);
②成员函数作友元函数
friend void goodGay::func();
③整个类作友元
friend class goodGay;