C++ 类中的static成员的使用及单例设计示例
文章目录
- static 静态成员变量
- static静态成员函数
- 单例设计模式
- 单例实现图片资源的封装(很实用)
静态成员:被static修饰的成员变量\函数
可以通过对象(对象.静态成员)、对象指针(对象指针->静态成员)、类访问(类名::静态成员)
static 静态成员变量
静态成员变量
- 存储在
数据段
(全局区,类似于全局变量),整个程序运行过程中只有一份内存
。 - 对比全局变量,它可以设定
访问权限
(public、protected、private),达到局部共享的目的。 - 必须初始化,
必须在类外面初始化
,初始化时不能带static,如果类的声明和实现分离(在实现.cpp中初始化)。
class Foo
{private://static静态变量无法在类中初始化static int ms_age;//与普通的全局变量的区别是 作用域 的权限不同,static类成员变量受到类的权限控制
public:void test(){cout << ms_age << endl;}//提供接口以用来访问与修改静态成员变量int& getMS_age(){return ms_age;}
};int Foo::ms_age = 1; //在类外使用::作用域解析初始化int main()
{Foo a;a.test();//两个对象共享同一个static静态对象,static位于全局区,整个程序运行时只存在这一个Foo b;b.test();/*mov dword ptr [Foo::age (07FF768C2C000h)],64hmov dword ptr [Foo::age (07FF768C2C000h)],0C8h*///调用的是同一个static静态成员变量a.getMS_age() = 100;b.getMS_age() = 200;a.test();b.test();return 0;
}
注意点:
- 在类中用static声明的变量是 静态变量,它存储在
数据段
(全局区,类似于全局变量),在整个程序运行时期只会有一份内存。 - 我们在一个类对象中修改的static静态成员变量会保持修改,则下一个类打印会发现static变量就不是以前的数据了,因为他类似于全局变量,相当于对全局变量进行修改。
两个类是否真的共用同一个static静态变量?我们来试验一下:
int main()
{Foo a;Foo b;//ms_age为类中的static静态成员变量a.ms_age=100;b.ms_age=200;return 0;
}
进入反汇编:
mov dword ptr [Foo::age (07FF768C2C000h)],64h
mov dword ptr [Foo::age (07FF768C2C000h)],0C8h
可以看到: 07FF768C2C000h 即是static变量的内存空间,a,b两个类的此变量的地址一样,说明他们共享同一个static变量,更加证明了类中的static变量其实就相当于全局变量,在整个程序运行期间不变且只有唯一的一个
。
那么? 类中的static静态成员变量和真正的全局变量 又有什么不同呢?
访问权限不同
:全局变量默认public,类中的static受到类的权限限定符的控制
class A{private:static int a; //位于private中,私有化
public:int& getA(){return a; //返回此static变量}
}int abc; //全局
int main()
{cout<<abc <<endl; //随意访问全局变量cout<<A::a<<endl; //无法直接访问类中的static//提供一个接口来访问getA()=100; //通过接口就可以访问了
类中static的成员变量的初始化
,注意:不可以直接在类内初始化,要在类外初始化,并且要去掉static修饰符,加上类名作用域::
class A{private:static int a; //位于private中,私有化
}
int A::a=0; //类外初始化
访问类中static变量的方法
: 我们要尽量使用类名直接访问static,因为他们本来就相当于全局的,否则容易产生混淆
int main()
{Foo* p = new Foo;p->ms_age = 100; //OK:指针访问a.ms_age = 200; //OK:栈对象访问Foo::ms_age = 300; //OK(推荐):类名直接访问return 0;
}
static静态成员函数
静态成员函数
- 内部
不能
使用this指针(this指针只能用在非静态成员函数内部) 不能是虚函数
(虚函数只能是非静态成员函数)- 内部
不能
访问非静态成员变量\函数,只能
访问静态成员变量\函数 - 非静态成员函数内部可以访问静态成员变量\函数
- 构造函数、析构函数不能是静态
- 当声明和实现分离时,实现部分不能带static
class Foo
{public://静态函数内存不位于类中,它在外部定义,与类无关static void test(){//静态成员变量可以随便访问cout << "<static> test():" << count << endl;//testTemp(); //cout << "age:" << age << endl; //非静态成员函数无法访问age属性,因为此age不属于任何对象,也无法访问非静态成员函数,同样它需要一个对象,但是此静态函数位于外部的全局区,它不属于类中,因此没有对象的概念,无法直接使用非静态成员函数。Foo a;a.age = 50; //但是可以创建一个临时的对象,利用对象来使用age成员变量a.testTemp();}void testTemp(){ cout << "testTemp()\n";}void test3(){//可以随意访问静态成员函数与变量count = 220;test();testTemp();}
private:static int count;int age = 10;
};int Foo::count = 0;Foo g;
int main()
{Foo a;Foo* b = new Foo;//任意类型创建的对象都可以正常访问静态函数a.test();b->test();g.test();//可以用类名直接指定此静态函数Foo::test();return 0;
}
static静态成员函数:相当于外部的全局的函数
,它虽然在类中,但是和类没有半毛钱关系,它的内存位于全局区。
- 无法使用this 指针,并且不能访问非静态成员变量\函数,只能访问静态static成员变量\函数。
class A{private:int a=20;static int b;
public:static void test(){this->a=50; //ERROR:this指针a=100; //ERROR: 相当于this指针A temp;temp.a=500; //OK:通过临时对象来访问temp的变量(闲的没事干)b=500; //OK:可以访问类的static静态成员函数,相当于全局变量fun(); //ERROR:无法访问非静态成员函数}void fun(); //一个非静态成员函数void fun2(){test(); //OK:可以使用非静态成员函数访问static成员函数}
}int A::b=20;
Error: 因为this指针表示某个类对象把它所在的地址传给了this指针,然后可以通过this访问其所有的变量,但是static静态成员函数位于全局区,所以根据this获取不到某个对象的地址,并且不使用this,想要获取类成员函数也是不可能的(除非你创建一个临时的对象),并且也无法访问非静态成员函数,相反则可以。
但可以访问类的static静态变量,和其他静态函数。
- 静态成员函数不能是虚函数,不能使构造函数,不能是析构函数
- 声明和实现分离时,实现的时候不要加static修饰符。
单例设计模式
什么是单例设计模式?
在我们的程序运行中只允许存在唯一一个对象,不管我们调用了多少个创建对象,我们始终都是得到唯一一个。
三要素:
构造函数必须私有化
必须具有一个唯一的static静态变量成为那个单例
提供公共的访问对象的接口
我们来一步步实现,我们既然要创建唯一一个对象,则不允许对象随意利用构造函数和析构函数。
注意: 所有的类的函数都是static的静态成员函数,这样我们就可以通过类名直接调用
- 把构造和析构设置为私有,并且创建唯一的单例成为静态成员变量
class Res
{public:
private:static Res* p; //这就是我们的单例对象,是唯一的Res(){}~Res() {} //防止在外部使用delete
};
- 单例的对象必须由我们亲自初始化: 给他一个构造对象的共有方法:p为空的时候才构造,这就实现了我们如果调用了多次创建对象的函数,但是只能创建一个对象。
static Res* CreatePoint()
{if (p == nullptr){p = new Res(); //调用构造函数}return p;
}
- 单例的销毁类成员函数:
static void Destroy()
{if (p != nullptr){delete p;p = nullptr; //注意此处重新置空,这样才能下一次创建对象}
}
- 测试:
int main(){//不管你创建多少次指针变量,他们都是同一个对象Res* p = Res::CreatePoint(); //只会创建这一个p->test();Res* p1 = Res::CreatePoint();p1->test();Res* p2 = Res::CreatePoint();p2->test();Res* p3 = Res::CreatePoint();p3->test();p->Destroy(); //只会销毁这一次p1->Destroy();p2->Destroy();p3->Destroy();//delete p; //析构私有,防止使用deletereturn 0;}
单例实现图片资源的封装(很实用)
在我们游戏制作中很好用,比如逆向做一个飞机大战,你可以封装这样一个单例,把所有的资源都加载进map容器中
(键–值对),根据类的enum枚举可以选取每一张特定的图片,大家仔细体会:
- 头文件
class Res
{public:enum class TYPE { plane, mm, bullet,往左走 };static Res* GetRes(); static void Draw(int x,int y,TYPE name);static void Draw动画(int x, int y, TYPE name, int frame);static void Destroy();
private:static Res* res; //单例对象static map<TYPE, IMAGE*> image;//私有构造函数和析构函数Res();~Res() {}
};
- 实现文件,我们使用了map集合。
/*
注意:首先在类的外部定义类的static成员变量
*/
map<Res::TYPE, IMAGE*> Res::image{};
Res* Res::res = nullptr; //初始化单例对象/*
注意: static成员函数的声明和实现分离时,实现要去掉static的修饰符
*/
Res* Res::GetRes(){if (res == nullptr){res = new Res; //创建单例对象}return res;
}
void Res::Draw(int x,int y,TYPE name){ //绘制静态图片putimage(x, y,GetRes()->image[name]);
}void Res::Draw动画(int x, int y, TYPE name, int frame){ //绘制连环画 :一系列动画的集合 /*连环画,每个图片有八张连续的动作图片,每一帧换一张图片。即用帧数乘以宽度可以得到下一张图片,利用putimage的裁剪图片的功能。*/putimage(x, y, 400, 300, GetRes()->image[name]+0, frame * 400, 0, SRCAND); //掩码图putimage(x, y, 400, 300, GetRes()->image[name]+1, frame * 400, 0, SRCPAINT); //原图
}
//单例的销毁
void Res::Destroy(){if (res != nullptr){delete res;res = nullptr;}
}Res::Res(){/*存储图片资源*/image[Res::TYPE::plane] = new IMAGE;loadimage(image[Res::TYPE::plane], "./Res/plane.jpg");image[Res::TYPE::bullet] = new IMAGE;loadimage(image[Res::TYPE::bullet], "./Res/bullet.jpg");image[Res::TYPE::mm] = new IMAGE;loadimage(image[Res::TYPE::mm], "./Res/mm.jpg");image[Res::TYPE::往左走] = new IMAGE[2];loadimage(image[Res::TYPE::往左走] + 0, "./Res/lefty.bmp");loadimage(image[Res::TYPE::往左走] + 1, "./Res/left.bmp");}
- 简单测试:
int main()
{initgraph(800, 600,SHOWCONSOLE);setbkcolor(WHITE);cleardevice();//你想画什么,直接Draw即可,在Draw的时候,会创建单例对象,且只创建一次Res::Draw(50, 100, Res::TYPE::plane);Res::Draw(50, 30, Res::TYPE::bullet);Res::Draw(200, 200, Res::TYPE::mm);Res::Destroy(); //销毁system("pause");closegraph();return 0;
}
C++ 类中的static成员的使用及单例设计示例相关推荐
- C++ 类中的static成员的初始化和特点
原文地址:https://blog.csdn.net/men_wen/article/details/64443040 C++ 类中的static成员的初始化和特点 在C++的类中有些成员变量初始化和 ...
- 【c++】为什么类中静态(static)成员不能在类的定义内初始化?
先看一段代码: #include <iostream> #include <string> using namespace std;class loopsaker{ publi ...
- C++ 类中的static成员的初始化
#include <iostream> using namespace std; class Test { public: Test() : y(1), r(y), d(3){} ...
- C++类中的static数据成员,static成员函数
文章转自:http://www.cnblogs.com/gysm/archive/2011/09/16/2179277.html 版权归原作者. C++类中谈到static,我们可以在类中定义stat ...
- C++知识点61——typename与class、模板编程与继承、模板类和友元、类模板与static成员
一.typename与class的异同 1.啥时候既可以使用typename,又可以使用class? 当表示模板参数的时候,二者没有区别 2.啥时候只能使用typename,不能使用class? 当模 ...
- 5006.c++类中使用static变量bug
c++类中使用static变量bug 正常情况,一般c++类中,不能使用static 定义成员,一般情况下在定义时编译器会进行报错.我呢由于c语言的使用习惯,没有太注意.在c++类的方法中,写代码时顺 ...
- C++中的static 成员变量的一些注意点
C++中的static成员变量主要用来为多个对象共享数据 例: #include <iostream> using namespace std; class Student{ public ...
- [FxCop.设计规则]16. 不要在封闭类中声明虚成员
16. 不要在封闭类中声明虚成员 翻译概述: 一条比较无聊的规则,并且VB.NET和C#编译器都已经内嵌的禁止代码违反这条规则. 引起的原因: 一个公共的封闭类型中包含虚成员.这条规则不检查d ...
- C#类中的internal成员可能是一种坏味道
前言 最近除了搞ASP.NET MVC之外,我也在思考一些编程实践方面的问题.昨天在回家路上,我忽然对一个问题产生了较为清晰的认识.或者说,原先只是有一丝细微的感觉,而现在将它和一些其他的方面进行了联 ...
最新文章
- python简笔画绘制 数据驱动绘图_pytorch visdom可视化工具学习—2—详细使用-2-plotting绘图...
- 如何在C中为一个数组分配空间?
- 中学计算机科学教育,计算机科学教育周 – Tsinghua International School 清华大学附属中学国际部...
- 组件分页_如何创建分页组件
- 一本通1629聪明的燕姿
- 股东痛斥联想管理层:都是帅哥 但业绩差
- TabBar与下拉列表访问数据与刷新
- 常用ror命令行工具
- 员工的不幸?还是公司的悲哀?
- 咖啡电子秤芯片方案SIC8833
- 用html设计logo,终于知道网页logo设计要点
- Python安装word2vec
- 侧入式LED背光源优缺点分析
- EndNote新手入门教程
- ACOUG年会归来(r11笔记第16天)
- 银行手机APP软件做性能测试吗,浅谈银行开放平台应用系统性能测试
- curl 支持ws吗_使用CURL请求WebService | 字痕随行
- 好用的磁盘清理软件Disk Diet for mac安装教程及功能介绍
- python中用plt画图
- axure的html按钮设置背景,Axure格式和样式功能详解
热门文章
- 图像滤镜艺术---(Sketch Filter)素描滤镜
- 快递物流查询,分析派件时效,查找正在派件的单号
- 一般硬盘读取速度和写入速度是多少
- eventhandler java_事件驱动模型的简单Java实现
- dwg图纸的预览功能
- Java + 腾讯企业邮箱 + javamail + SSL 发送邮件(转载:http://www.cnblogs.com/LUA123/p/5575134.html)
- STL(五)之智能指针剖析
- fone喜获“2018中国企业绩效管理信息化最佳产品奖”
- session与cookie有效时间
- Map接口总结与HashMap源码分析