目录
1.静态成员
1.1 静态成员变量
1.2 静态成员函数
1.3 const静态成员属性
2.静态成员实现单例模式
3.成员变量和函数的存储
4.this指针及其使用
学习内容:
#include
using namespace std;
#include
#include
/****************************************************************************************************
* 38.静态成员:在类定义中,它的成员(包括成员变量和成员函数),这些成员可以用关键字 static 声明为静态的,称为静态成员。
* 不管这个类创建了多少个对象,静态成员只有一个拷贝,这个拷贝被所有属于这个类的对象共享(对象自己的成员属于对象独享)。
* ① 静态成员变量:static修饰的类的成员变量。
* > 静态成员变量,属于某个类,不属于对象(所以在为对象分配空间中不包括静态成员所占空间,除此之外,之前讲过,
* 成员函数不占类的空间),但对类所有对象共享;
* > 静态成员变量,是在编译阶段就分配空间,即对象还没有创建时,就已经分配空间,所以静态成员变量必须在类中声明
* (确保属于某类),在类外定义(确保提前分配空间,不管是公有还是私有数据都需要在类外定义);
* > 静态成员变量可以通过类名或者对象名来调用;
* > 静态成员变量也有访问权限,类外不能访问私有成员。
* ② 静态成员函数:static修饰的类的成员函数。
* > 静态成员函数,属于类,不属于对象,既可通过类名调用,也可以通过对象名来调用;
* > 普通成员函数,属于对象,不属于类,只能通过对象来调用,因为普通成员函数依赖于对象;
* > 静态成员函数使用方式和静态成员变量一样,同样在对象没有创建前;
* > 静态成员函数:只能访问静态成员变量,不能访问普通成员变量,它存在的意义,不在于信息共享,数据沟通,而在于
* 管理静态数据成员,完成对静态数据成员的封装;
* > 普通成员函数可访问静态成员变量,也可以访问非静态成员变量;
* > 类外无法访问私有静态成员函数。
* ③ const静态成员属性:如果一个类的成员,既要实现共享,又要实现不可改变,那就用 static const 修饰。定义静态
* const 数据成员时,最好在类内部初始化。
****************************************************************************************************/
class Person09{
public:
static int sNum; //类的静态公有成员属性
int mParam; //类的公有成员属性
// static const int mShare = 10; // 只读静态属性
const static int mShare = 10; // 与上句相同,两种定义方法都可
//普通成员函数可以访问 static 和 non-static 成员属性
//静态成员函数只能通过对象来调用,在对象没有初始化之前,无法使用
void changeParam1(int param){
mParam = param; // non-static
sNum = param; // static
cout << "mParam = " << mParam << endl;
cout << "sNum = " << sNum << endl;
}
//静态成员函数只能访问 static 成员属性
static void changeParam2(int param){
// mParam = param; // 无法访问非static属性
sNum = param;
}
private:
//类的静态私有成员属性
static int sOther;
static void changeParam3(int param){
//mParam = param; //无法访问
sNum = param;
}
};
//类外定义()并初始化,初始化时不加 static
int Person09::sNum = 0;
int Person09::sOther = 0;
void test32(){
//1. 通过类名直接访问
Person09::sNum = 100;
cout << "Person09::sNum:" << Person09::sNum << endl;
//2. 通过对象访问
Person09 p1, p2;
p1.sNum = 200;
cout << "p1.sNum:" << p1.sNum << endl; // 输出:200
cout << "p2.sNum:" << p2.sNum << endl; // 输出:200,说明在p1.sNum = 200;这一步实现了数据共享
//3. 静态成员变量也有访问权限,类外不能访问私有成员,所以下面语句均报错
// cout << "Person09::sOther:" << Person09::sOther << endl;
//cout << "p2.sOther:" << p2.sOther << endl;
//4. 普通成员函数的调用:只能通过对象来调用
// Person09::changeParam1(5); // 报错:不能通过类名调用
// error: call to non-static member function without an object argument
p2.changeParam1(Person09::sNum); // 正确做法,
// 但是如果我们想在未实例化对象之前使用静态私有成员变量怎么办呢?——使用类的静态成员函数
//5. 类名直接调用静态成员函数
Person09::changeParam2(100);
//6. 通过对象调用静态成员函数
p2.changeParam2(200);
//7. 静态成员函数也有访问权限
// Person09::changeParam3(100); //报错:类外无法访问私有静态成员函数
//error: 'changeParam3' is a private member of 'Person09'
// p3.changeParam3(200); // 报错,错误原因同上。
// 8.const静态成员属性
cout << "Person09::mShare = " << Person09::mShare << endl; // 输出10
// Person09::mShare = 20; // 报错:只读数据不可修改
// error: cannot assign to variable 'mShare' with const-qualified type 'const int'
}
/****************************************************************************************************
* 随堂练习04 使用静态成员变量,统计类被实例化为对象的个数:
****************************************************************************************************/
class Person10{
public:
static int count;
Person10()
{
cout << "无参构造函数!" << endl;
count++;
}
Person10(const Person10 &p)
{
cout << "拷贝构造函数!" << endl;
}
~Person10()
{
count--;
cout << "析构函数!" << endl;
}
};
int Person10::count = 0;
void test33()
{
Person10 per01;
Person10 per02;
{ // 复合语句
Person10 per03;
Person10 per04;
cout << "对象的个数:" << Person10::count << endl;
}
cout << "对象的个数:" << Person10::count << endl;
}
/****************************************************************************************************
* 39.静态成员实现单例模式:单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模
* 式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。所以,如果希望在系
* 统中某个类的对象只能存在一个,单例模式是最好的解决方案。(一句话,单例模式就是一个类只有一个对象)
* Singleton(单例):在单例类的内部实现只生成一个实例,同时它提供一个静态的 getInstance()工厂方法,让客户可以
* 访问它的唯一实例;
* 为了防止在外部对其实例化,将其默认构造函数和拷贝构造函数设计为私有;
* 在单例类内部定义了一个 Singleton 类型的静态对象,作为外部共享的唯一实例;
*
* 随堂练习05:用单例模式,模拟公司员工使用打印机场景,打印机可以打印员工要输出的内容,并且可以累积打印机使用次数。
* step1:定义一个静态的对象指针变量,保存唯一实例地址;
* step2:提供一个静态方法,让用户可以访问它的唯一实例;
* step3:防止该类被实例化为多个对象,将构造函数(无参、有参、拷贝构造函数)私有化;
* step4:设置功能函数,实现需求功能。
****************************************************************************************************/
class Printer{
public:
static Printer* getInstance(){ return pPrinter;} // 提供一个方法,用于获得单例指针
// 功能函数
void PrintText(string text){
cout << "打印内容:" << text << endl;
cout << "已打印次数:" << ++mTimes << endl;
cout << "--------------" << endl;
}
private:
Printer(){ mTimes = 0; } // 无参构造函数
Printer(const Printer&){} // 拷贝构造函数
private:
static Printer* pPrinter; // 静态自身对象指针变量的声明,用于保存唯一实例的地址
int mTimes;
};
Printer* Printer::pPrinter = new Printer; // 静态对象的定义,这就是那唯一的对象(类的实例化)
void test34(){
Printer* printer1 = Printer::getInstance();
printer1->PrintText("入职报告!");
printer1->PrintText("入职合同!");
printer1->PrintText("工作绩效!");
Printer* printer2 = Printer::getInstance();
printer2->PrintText("辞职申请!");
printer2->PrintText("离职报告!");
}
/****************************************************************************************************
* 40.成员变量和函数的存储:
* ~废话:在 C 语言中,“分开来声明的,也就是说,语言本身并没有支持“数据”和“函数”之间的关联性,我们把这种程序方法称
* 为“程序性的”,由一组“分布在各个以功能为导航的函数中”的算法驱动,它们处理的是共同的外部数据。
* *了解:C++实现了“数据”和“处理数据的操作(函数)”的“封装”,但是将“数据”和“函数”分开存储,数据存储在每个对象中,方
* 法是对象共有的,都存放在代码区。
* ~废话:C++中的非静态数据成员直接内含在类对象中,就像 C中结构体(c struct)一样。成员函数(member function)虽
* 然内含在 class 声明之内,却不出现在对象中。每一个非内联成员函数(non-inline member function)只会诞生一份函数实例:
* 如下:静态数据成员并不保存在类对象中、非静态成员函数不保存在类对象中、静态成员函数也不保存在类对象中;
* 结论::C++类对象中的变量和函数是分开存储。
****************************************************************************************************/
class MyClass01{
public:
int mA;
};
class MyClass02{
public:
int mA;
static int sB;
};
class MyClass03{
public:
void printMyClass(){ cout << "hello world!" << endl;}
public:
int mA;
static int sB;
};
class MyClass04{
public:
void printMyClass(){ cout << "hello world!" << endl;}
static void ShowMyClass(){ cout << "hello world!" << endl;}
public:
int mA;
static int sB;
};
void test35()
{
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
}
/****************************************************************************************************
* 通过上例我们知道,C++的数据和操作是分开存储,并且每一个非内联成员函数(non-inline member function)只会诞生
* 一份函数实例,也就是说多个同类型的对象会共用一块(函数)代码,那么问题是:这一块代码是如何区分是哪个对象调用自己的呢?
*
* 41.this指针:C++通过 this 指针(系统给我们做),解决上述问题。this指针指向被调用的成员函数所属的对象。
* C++规定,this 指针是隐含在对象成员函数内的一种指针。当一个对象被创建后,它的每一个成员函数都含有一个系统自动生
* 成的隐含指针 this,用以保存这个对象的地址,也就是说虽然我们没有写上 this 指针,编译器在编译的时候也会自动加上的(所
* 以我们一般都不写this,而是让系统进行默认设置)。this指针有以下特点:
* ① this 指针也称为“指向本对象的指针”,this 指针并不是对象的一部分,不会影响 sizeof(对象) 的结果;
* ② this 指针是C++实现封装的一种机制,它将对象和该对象调用的成员函数连接在一起;
* ③ this 指针永远指向当前对象,成员函数通过 this 指针即可知道操作的是哪个对象的数据。
* ④ this 指针是一种隐含指针,它隐含于每个类的非静态成员函数中。
* ⑤ this 指针无需定义,直接使用即可;
* ⑥ 注意:静态成员函数内部没有 this 指针(这是因为静态成员函数属于类,不属于对象,而this指针指向的是对象的地址,
* 所以静态成员函数操作普通成员变量时,系统不知道该操作哪个对象的成员变量),所以静态成员函数不能操作非静态(普通)成员
* 变量。
* 为了便于理解,我们用C和C++实现同样的功能,以便观察C++编译器对普通成员函数的内部处理:
* > 如下:当一个对象调用setNum()方法时,会在setNum()方法中产生一个this指针,this指针指向调用该方法的对象。
****************************************************************************************************/
/****** C++ ******/
class MyTest01
{
private:int m_a; // 4字节
public:
MyTest01(int a){m_a = a;} // 该函数在此相当于一个set函数,
// m_a = a;相当于下面两句:
// this = &某调用该函数的对象; &为取地址符
// this->m_a = a;
int getA(){return m_a;} // C++中ruturn m_a 就相当用于C语言中的return pThis->mA;
static void printTags(){cout << "这是C++中的MyTest01类" << endl;}
};
void test36(){
MyTest01 a(10);
cout << "a.getA() = " << a.getA() << endl;
MyTest01::printTags();
}
/****** C ******/
struct MyTest02{int mA;}; // 结构体
void initMyTest02(MyTest02 *pThis, int a){pThis->mA = a;}
int myTest02_getA(MyTest02 *pThis){return pThis->mA;}
void myTest02_printTags(){printf("这是C中的MyTest02类");}
void test37(){
MyTest02 mmA;
initMyTest02(&mmA, 10);
printf("myTest02_getA(&mmA) = %d\n", myTest02_getA(&mmA));
myTest02_printTags();
}
/****************************************************************************************************
* 42.this指针的使用:
* ① 当形参和成员变量同名时,可用 this 指针来区分;
* ② 在类的非静态成员函数中返回对象本身,可使用 return *this,其中*this就代表当前调用该函数的对象本身;
****************************************************************************************************/
class Person11{
public:
string name;
int age;
public:
//1. 当形参名和成员变量名一样时,this 指针可用来区分
Person11(string name,int age){
//name = name; // 赋值不成功
//age = age; // 输出错误,这只表示形参给自己赋值
this->name = name;
this->age = age;
}
//2. 在类的非静态成员函数中返回对象本身,可使用 return *this
Person11& myCout(char *sstr)
{
cout << sstr;
return *this; // 返回调用该函数的对象本身
}
//2. 返回对象本身的引用
//重载赋值操作符
//其实也是两个参数,其中隐藏了一个 this 指针
Person11 PersonPlusPerson(Person11& person){ // 返回对象类型的非静态成员函数
string newname = this->name + person.name;
int newage = this->age + person.age;
Person11 newperson(newname, newage);
return newperson;
}
void ShowPerson11(){
cout << "Name:" << name << " Age:" << age << endl;
}
};
//3. 成员函数和全局函数(Perosn 对象相加)
Person11 PersonPlusPerson(Person11& p1,Person11& p2){
string newname = p1.name + p2.name;
int newage = p1.age + p2.age;
Person11 newperson(newname,newage);
return newperson;
}
void test38(){
Person11 person("John",100);
person.ShowPerson11();
person.myCout("haha").myCout("hehe").myCout("xixi").ShowPerson11(); // 这样就可以重复调用某函数了
// person.myCout("haha") == person,person.myCout("haha").myCout("hehe") == person……
cout << "-------------------" << endl;
Person11 person1("John",20);
Person11 person2("001", 10);
//1.全局函数实现两个对象相加
Person11 person3 = PersonPlusPerson(person1, person2);
person1.ShowPerson11();
person2.ShowPerson11();
person3.ShowPerson11();
cout << "------------------" << endl;
//2. 成员函数实现两个对象相加
Person11 person4 = person1.PersonPlusPerson(person2);
person4.ShowPerson11();
}
int main()
{
// test32();
// test33();
// test34();
// test35();
// test36();
// test37();
// test38();
return 0;
}