C++静态成员和 this 指针详解
- 引言
- 一、静态成员
- 1.1、静态成员变量
- 1.2、静态成员变量的使用示例
- 1.3、静态成员函数
- 1.4、单例模式设计
 
- 二、C++面向对象模型
- 2.1、成员变量和函数的存储
- 2.2、this 指针
- 2.3、this 指针的应用
- 2.4、const修饰成员函数
 
- 总结
引言
💡 作者简介:专注于C/C++高性能程序设计和开发,理论与代码实践结合,让世界没有难学的技术。包括C/C++、Linux、MySQL、Redis、TCP/IP、协程、网络编程等。
👉
🎖️ CSDN实力新星,社区专家博主
👉
🔔 专栏介绍:从零到c++精通的学习之路。内容包括C++基础编程、中级编程、高级编程;掌握各个知识点。
👉
🔔 专栏地址:C++从零开始到精通
👉
🔔 博客主页:https://blog.csdn.net/Long_xu
🔔 上一篇:【028】C++ 类和对象的 构造函数、析构函数、拷贝构造、初始化列表 详解(最全讲解)
一、静态成员
C++静态成员是指属于类而不是属于对象的成员。与普通成员变量和函数不同,静态成员变量和函数在内存中只有一份副本,无论类实例化了多少次,它们都是共享的。
在类的定义中,它的成员(包括成员变量、成员函数)可以使用关键字static声明为静态的,称为静态成员。不管这个类创建了多少个对象,静态成员只有一个拷贝,这个拷贝被所有属于这个类的对象共享。使用静态成员可以实现对类级别的操作。
使用方法:
-  声明静态成员变量或函数:在类声明中使用 static 关键字来声明静态成员变量或函数。 
-  定义静态成员变量或函数:在类外部用作用域解析运算符(::)定义静态成员变量或函数。 
1.1、静态成员变量
static修饰的静态成员属于类而不是对象,所有的对象共享一份静态成员数据。例如:
class Data{
private:
	int a;
	int b;
	int c;
	static int d;
};
实例化时它的内存布局:
 
 静态成员变量中,需要清楚以下几点:
- static修饰的成员,定义类的时候必须分配空间。
- static修饰的静态成员数据必须类中定义类外初始化。
示例:
#include <iostream>
using namespace std;
class Data{
private:
	// 普通成员变量
	int a;
	int b;
	int c;
	// 类中定义静态成员变量
	static int d;
};
// 类外初始化静态成员变量,不用加static
int Data::d=100;
int main()
{
	// 静态成员变量可以通过类名称直接访问
	cout<<Data::d<<endl;
	// 静态成员变量可以通过对象访问(对象间共享)
	Data ob1;
	cout<<ob1.d<<endl;
	Data ob2;
	Ob2.d=200;
	Data ob3;
	Ob3.d=300;
	// 共享
	cout<<ob1.d<<endl;
	// 静态成员变量可以通过类名称直接访问
	cout<<Data::d<<endl;
	return 0;
}
输出:
100
100
300
300
1.2、静态成员变量的使用示例
使用静态成员数据统计对象的个数。
#include <iostream>
using namespace std;
class Data{
public:
	int mA;
	static int count;
public:
	Data()
	{
		mA=0;
		count++;
	}
	Data(int num)
	{
		mA=num;
		count++;
	}
	Data(const Data &ob)
	{
		mA=ob.mA;
		count++;
	}
	~Data()
	{
		count--;
	}
};
int Data::count=0;
int main()
{
	Data ob1;
	Data ob2(100);
	Data ob3=ob2;
	cout<<"对象个数:"<<Data::count<<endl;
	{
		Data ob4;
		Data ob5;
		cout<<"对象个数:"<<Data::count<<endl;
	}
	cout<<"对象个数:"<<Data::count<<endl;
	return 0;
}
输出:
对象个数:3
对象个数:5
对象个数:3
这里使用的是public权限的静态成员变量,如果是private或protected权限的,必须使用函数才能访问变量。
1.3、静态成员函数
静态成员函数属于类而不是对象,所有的对象共享。
class Data{
	static void func()
	{
	}
};
静态成员函数中,需要清楚以下几点:
- 静态成员函数可以直接通过类名称访问。
- 静态成员函数内只能操作静态成员变量。因为静态成员属于类,在类定义时就创建了,所有开辟了内存空间,可以访问,但是普通成员是类实例化为对象是才开辟空间,所有静态成员函数内只能操作静态成员变量。
示例:
#include <iostream>
using namespace std;
class Data{
private:
	int data;
	static int mA;
public:
	static int getA()
	{
		// data = 200;//error,不可以操作普通变量
		return mA;
	}
};
int Data::mA=100;
int main()
{
	// 静态成员函数可以直接通过类名称访问。
	cout<<Data::getA()<<endl;
}
1.4、单例模式设计
步骤:
- 防止类在外界实例化对象,将构造函数私有化(包括无参构造、有参构造、拷贝构造)。
- 定义一个静态的指针变量,保存唯一实例的地址。
- 获取唯一的实例地址;提供静态成员函数。
- 定义任务函数。
示例:
#include <iostream>
using namespace std;
class SingleTon {
	// 将构造函数私有化,防止类在外界实例化对象
private:
	SingleTon() {}
	SingleTon(const SingleTon &ob) {}
	~SingleTon() {}
private:
	// 定义一个静态指针变量,保存唯一实例的地址
	static SingleTon* const p;
public:
	// 提供一个静态成员函数,返回唯一实例的地址
	static SingleTon* getSingleTon(void)
	{
		return p;
	}
	// 定义的任务函数
	void printData(const char *str)
	{
		cout << "打印:" << str << endl;
	}
};
// 实例化对象
SingleTon* const SingleTon::p = new SingleTon;
int main()
{
	SingleTon *p1 = SingleTon::getSingleTon();
	p1->printData("hello sinleton");
	p1->printData("Lion");
	p1->printData("Tom");
	SingleTon *p2 = SingleTon::getSingleTon();
	p2->printData("hi sinleton");
	p2->printData("Long");
	p2->printData("Lucien");
	return 0;
}
二、C++面向对象模型
2.1、成员变量和函数的存储
C++实现了“封装”、“数据”和数据处理操作(函数)分开存储。C++中的非静态成员变量直接内含在类对象中,成员函数虽然内含在class声明中,却不出现在对象中,每一份非内联成员函数只会产生一份函数实例。
 例如:
class Data{
public:
	char a;
	int b;
	static int c;
public:
	void func(){}
};

 sizeof(Data)的大小只是a和b所占用空间大小(类的对象所占空间大小)。
示例:
#include <iostream>
using namespace std;
class Data01{
public:
	int mA;
};
class Data02{
public:
	int mA;
	static int data;
};
class Data03{
public:
	void func()
	{
		cout<<"Data02 func"<<endl;
	}
public:
	int mA;
	static int data;
};
class Data02{
public:
	void func()
	{
		cout<<"Data02 func"<<endl;
	}
	static void func02()
	{
		cout<<"Data02 static func02"<<endl;
	}
public:
	int mA;
	static int data;
};
int main()
{
	Data ob01;
	Data02 ob02;
	Data03 ob03;
	Data04 ob04;
	cout<<"sizeof(Data): "<<sizeof(ob01)<<endl;
	cout<<"sizeof(Data02): "<<sizeof(ob02)<<endl;
	cout<<"sizeof(Data03): "<<sizeof(ob03)<<endl;
	cout<<"sizeof(Data04): "<<sizeof(ob04)<<endl;
	return 0;
}
输出:
sizeof(Data): 4
sizeof(Data02): 4
sizeof(Data03): 4
sizeof(Data04): 4
可以看到:C++类对象中的变量和函数是分开存储的。
2.2、this 指针
C++的数据和操作也是分开存储的,并且每一个非内联成员函数只会诞生一份函数实例;也就是说多个类型相同的对象会公用一块代码;那么这块代码如何区分哪个对象调用自己呢?
 
C++通过提供特殊的对象指针:this指针,解决上述的问题;this 指针指向被调用的成员函数所属的对象。成员函数通过this指针即可 知道操作的是哪个对象的数据。this指针是一种隐式指针,隐含于每个类的非静态成员函数中。this指针无需定义,直接使用即可。
 
 注意:静态成员函数内部没有this指针(因为this指针保存的是对象地址,而静态成员函数属于类,在对象出现之前就已经存在),静态成员函数不能操作非静态成员变量。
2.3、this 指针的应用
(1)函数形参和成员同名可以使用this指针解决。
#include <iostream>
using namespace std;
class Data{
public:
	int num;
public:
	Data(int num)
	{
		this->num=num;
		cout<<this<<endl;
	}
};
int main()
{
	Data ob(100);
	cout<<ob.num<<endl;
	cout<<&ob<<endl;
	return 0;
}
输出:
0x61fe8c
100
0x61fe8c
(2)this 指针来完成链式操作。
#include <iostream>
using namespace std;
class Data{
	Data& printData(const char *str)
	{
		cout<<str<<" ";
		return *this;//返回调用该成员函数的对象
	}
}
int main()
{
	Data().printData("hello,").printData("Lion").printData("Long");
}
输出:
hello, Lion Long
2.4、const修饰成员函数
用const修饰成员函数时,const修饰this指针指向的内存区域,成员函数体内不可以修改本类中的任何普通成员变量,除非成员变量类型前面用mutable修饰。
示例:
#include <iostream>
using namespace std;
class Data{
public:
	int a;
	int b;
	mutable int c;
public:
	Data(int a,int b,int c)
	{
		this->a=a;
		this->b=b;
		this->c=c;
	}
	void show(void) const
	{
		//a=100;// error
		c=500;// OK
		cout<<a<<" "<<b<<" "<<c<<endl;
	}
};
int main()
{
	Data obj(100,200,300);
	obj.show();
}
输出:
100 200 500
总结
-  静态成员是类的属性,属于整个类而不是类的对象。可以在任何对象中访问它们,但不需要创建实例即可使用它们。 
-  使用静态成员变量时,必须在其前面加上“类名::”来指定其作用域。例如:ClassName::staticMemberVariable。 
-  静态成员函数也属于整个类,可以直接通过类名调用,并且不能访问非静态成员变量和函数。 
-  静态成员变量只有一个副本,在所有对象之间共享。当类的一个对象修改了这个变量时,其他对象也会受到影响。 
-  this 指针是指向当前对象的指针,在成员函数内部使用。它可以用来访问该对象的非静态成员变量和函数。 
-  在静态成员函数中不能使用 this 指针,因为静态函数没有 this 指针。 



















