C++静态成员和 this 指针详解
引言
💡 作者简介:专注分享高性能服务器后台开发技术知识,涵盖多个领域,包括C/C++、Linux、网络协议、设计模式、中间件、云原生、数据库、分布式架构等。目标是通过理论与代码实践的结合,让世界上看似难以掌握的技术变得易于理解与掌握。
💡 公众号:Lion 莱恩呀
👉
🎖️ CSDN实力新星、专家博主,阿里云博客专家、华为云云享专家
👉
🔔 专栏介绍:从零到c++精通的学习之路。内容包括C++基础编程、中级编程、高级编程;掌握各个知识点。
👉
🔔 专栏地址:C++从零开始到精通
👉
🔔 博客主页:https://blog.csdn.net/Long_xu
🔔 上一篇:【028】C++ 类和对象的 构造函数、析构函数、拷贝构造、初始化列表 详解(最全讲解)
🔔 下一篇:【030】C++类和对象之友元(friend)详解
一、静态成员
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
指针。