文章目录

  • 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静态成员变量和真正的全局变量 又有什么不同呢?

  1. 访问权限不同:全局变量默认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;    //通过接口就可以访问了
  1. 类中static的成员变量的初始化,注意:不可以直接在类内初始化,要在类外初始化,并且要去掉static修饰符,加上类名作用域::
class A{private:static int a;   //位于private中,私有化
}
int A::a=0;    //类外初始化
  1. 访问类中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静态成员函数:相当于外部的全局的函数它虽然在类中,但是和类没有半毛钱关系,它的内存位于全局区。


  1. 无法使用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静态变量,和其他静态函数。

  1. 静态成员函数不能是虚函数,不能使构造函数,不能是析构函数

  1. 声明和实现分离时,实现的时候不要加static修饰符。

单例设计模式

什么是单例设计模式?
在我们的程序运行中只允许存在唯一一个对象,不管我们调用了多少个创建对象,我们始终都是得到唯一一个。

三要素:

  1. 构造函数必须私有化
  2. 必须具有一个唯一的static静态变量成为那个单例
  3. 提供公共的访问对象的接口

我们来一步步实现,我们既然要创建唯一一个对象,则不允许对象随意利用构造函数和析构函数。

注意: 所有的类的函数都是static的静态成员函数,这样我们就可以通过类名直接调用

  1. 把构造和析构设置为私有,并且创建唯一的单例成为静态成员变量
class Res
{public:
private:static Res* p;      //这就是我们的单例对象,是唯一的Res(){}~Res() {}    //防止在外部使用delete
};
  1. 单例的对象必须由我们亲自初始化: 给他一个构造对象的共有方法:p为空的时候才构造,这就实现了我们如果调用了多次创建对象的函数,但是只能创建一个对象。
static Res* CreatePoint()
{if (p == nullptr){p = new Res();    //调用构造函数}return p;
}
  1. 单例的销毁类成员函数:
static void Destroy()
{if (p != nullptr){delete p;p = nullptr;      //注意此处重新置空,这样才能下一次创建对象}
}
  1. 测试:
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枚举可以选取每一张特定的图片,大家仔细体会:

  1. 头文件
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() {}
};
  1. 实现文件,我们使用了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");}
  1. 简单测试:
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成员的使用及单例设计示例相关推荐

  1. C++ 类中的static成员的初始化和特点

    原文地址:https://blog.csdn.net/men_wen/article/details/64443040 C++ 类中的static成员的初始化和特点 在C++的类中有些成员变量初始化和 ...

  2. 【c++】为什么类中静态(static)成员不能在类的定义内初始化?

    先看一段代码: #include <iostream> #include <string> using namespace std;class loopsaker{ publi ...

  3. C++ 类中的static成员的初始化

    #include <iostream> using namespace std; class Test { public:     Test() : y(1), r(y), d(3){} ...

  4. C++类中的static数据成员,static成员函数

    文章转自:http://www.cnblogs.com/gysm/archive/2011/09/16/2179277.html 版权归原作者. C++类中谈到static,我们可以在类中定义stat ...

  5. C++知识点61——typename与class、模板编程与继承、模板类和友元、类模板与static成员

    一.typename与class的异同 1.啥时候既可以使用typename,又可以使用class? 当表示模板参数的时候,二者没有区别 2.啥时候只能使用typename,不能使用class? 当模 ...

  6. 5006.c++类中使用static变量bug

    c++类中使用static变量bug 正常情况,一般c++类中,不能使用static 定义成员,一般情况下在定义时编译器会进行报错.我呢由于c语言的使用习惯,没有太注意.在c++类的方法中,写代码时顺 ...

  7. C++中的static 成员变量的一些注意点

    C++中的static成员变量主要用来为多个对象共享数据 例: #include <iostream> using namespace std; class Student{ public ...

  8. [FxCop.设计规则]16. 不要在封闭类中声明虚成员

    16.     不要在封闭类中声明虚成员 翻译概述: 一条比较无聊的规则,并且VB.NET和C#编译器都已经内嵌的禁止代码违反这条规则. 引起的原因: 一个公共的封闭类型中包含虚成员.这条规则不检查d ...

  9. C#类中的internal成员可能是一种坏味道

    前言 最近除了搞ASP.NET MVC之外,我也在思考一些编程实践方面的问题.昨天在回家路上,我忽然对一个问题产生了较为清晰的认识.或者说,原先只是有一丝细微的感觉,而现在将它和一些其他的方面进行了联 ...

最新文章

  1. python简笔画绘制 数据驱动绘图_pytorch visdom可视化工具学习—2—详细使用-2-plotting绘图...
  2. 如何在C中为一个数组分配空间?
  3. 中学计算机科学教育,计算机科学教育周 – Tsinghua International School 清华大学附属中学国际部...
  4. 组件分页_如何创建分页组件
  5. 一本通1629聪明的燕姿
  6. 股东痛斥联想管理层:都是帅哥 但业绩差
  7. TabBar与下拉列表访问数据与刷新
  8. 常用ror命令行工具
  9. 员工的不幸?还是公司的悲哀?
  10. 咖啡电子秤芯片方案SIC8833
  11. 用html设计logo,终于知道网页logo设计要点
  12. Python安装word2vec
  13. 侧入式LED背光源优缺点分析
  14. EndNote新手入门教程
  15. ACOUG年会归来(r11笔记第16天)
  16. 银行手机APP软件做性能测试吗,浅谈银行开放平台应用系统性能测试
  17. curl 支持ws吗_使用CURL请求WebService | 字痕随行
  18. 好用的磁盘清理软件Disk Diet for mac安装教程及功能介绍
  19. python中用plt画图
  20. axure的html按钮设置背景,Axure格式和样式功能详解

热门文章

  1. 图像滤镜艺术---(Sketch Filter)素描滤镜
  2. 快递物流查询,分析派件时效,查找正在派件的单号
  3. 一般硬盘读取速度和写入速度是多少
  4. eventhandler java_事件驱动模型的简单Java实现
  5. dwg图纸的预览功能
  6. Java + 腾讯企业邮箱 + javamail + SSL 发送邮件(转载:http://www.cnblogs.com/LUA123/p/5575134.html)
  7. STL(五)之智能指针剖析
  8. fone喜获“2018中国企业绩效管理信息化最佳产品奖”
  9. session与cookie有效时间
  10. Map接口总结与HashMap源码分析