【C++深度剖析教程5】C++中类的静态成员函数

  • 学习交流加(可免费帮忙下载CSDN资源):
  • 个人微信: liu1126137994
  • 学习交流资源分享qq群1(已满): 962535112
  • 学习交流资源分享qq群2(已满): 780902027
  • 学习交流资源分享qq群3:893215882

在上一篇文章中我们讲了C++中类的静态成员变量,用类的静态成员变量实现了统计程序运行期间的某个类的对象的数目(不清楚的可以点击连接查看上一篇文章 C++中类的静态成员变量)。

我们回顾一下客户的需求:

  • 统计在程序运行期间某个类的对象的数目
  • 保证程序的安全性(不能使用全局变量)
  • 随时可以获取当前对象的数目(完成了么?)

看当时的代码:

#include <stdio.h>class Test
{
private:static int cCount;
public:Test(){cCount++;}~Test(){--cCount;}int getCount(){return cCount;}
};int Test::cCount = 0;Test gTest;int main()
{Test t1;Test t2;printf("count = %d\n", gTest.getCount());printf("count = %d\n", t1.getCount());printf("count = %d\n", t2.getCount());Test* pt = new Test();printf("count = %d\n", pt->getCount());delete pt;printf("count = %d\n", gTest.getCount());return 0;
}

很明显,我们获取对象的数目是需要调用getCount这个函数,每次调用都需要某一个对象来调用,比如:gTest.getCount(),那么问题来了,如果整个程序的对象的个数为0呢?那么岂不是没法调用getCount这个函数,也就没法知道对象的个数(别人是不知道你的对象的个数为0的)?

可不可以这样呢?我把静态成员变量cCount变为public,然后直接让Test(作用域调用)这个类来调用?先上一波代码看看:

#include <stdio.h>class Test
{
public:static int cCount;
public:Test(){cCount++;}~Test(){--cCount;}int getCount(){return cCount;}
};int Test::cCount = 0;int main()
{printf("count = %d\n", Test::cCount);//Test::cCount = 1000;//printf("count = %d\n", Test::cCount);return 0;
}

此代码与上面的代码的不同的是cCount变量变为public便于Test(作用域访问)类调用,没有任何的对象了,那么编译运行,输出结果应该是:
count = 0

说明这样做,符合了我们的需求了。但是,不要高兴的太早了,我们这样真的可以么?加入我把代码中的被注释的那两行加上(下面这两行):

     Test::cCount = 1000;printf("count = %d\n", Test::cCount);

再次运行会发生什么呢?我们编译运行,结果输出为:

 count = 0count = 1000

那么多问题来了,客户现在懵逼了,他会以为对象的个数有1000个,所以,这也是一个大的Bug啊,从本上讲,这样做也是无法满足客户的需求的,而且将cCount变为public,本身就是无法保证静态成员变量的安全性,我们需要什么?

  • 不依赖对象就可以访问静态成员变量
  • 必须保证静态成员变量的安全性
  • 方便快捷得获取静态成员变量的值

那么静态成员函数就要出马了:
静态成员函数的定义:
直接通过static关键字修饰成员函数即可

为了便于理解,我们先上一段代码来理解一下静态成员函数的性质:

#include <stdio.h>class Demo
{
private:int i;
public:int getI();static void StaticFunc(const char* s);static void StaticSetI(Demo& d, int v);
};int Demo::getI()
{return i;
}void Demo::StaticFunc(const char* s)
{printf("StaticFunc: %s\n", s);
}void Demo::StaticSetI(Demo& d, int v)
{d.i = v;
}int main()
{Demo::StaticFunc("main Begin...");Demo d;d.StaticSetI(d, 20);printf("d.i = %d\n", d.getI());Demo::StaticSetI(d, 10);printf("d.i = %d\n", d.getI());Demo::StaticFunc("main End...");return 0;
}

以上代码运行的结果为:

StaticFunc: main Begin...
d.i = 20
d.i = 10
StaticFunc: main End...

由代码看到两个静态成员函数:

static void StaticFunc(const char* s);
static void StaticSetI(Demo& d, int v);

类调用静态成员函数:

Demo::StaticFunc("main Begin...");

对象调用静态成员函数:

d.StaticSetI(d, 20);

可以看出静态成员函数的性质大体如下:

  • 静态成员函数是类中特殊的成员函数
  • 静态成员函数属于整个类所有
  • 可以通过类名(作用域访问)直接访问公有静态成员函数
  • 可以通过对象名访问公有静态成员函数

而且通过这段代码:

void Demo::StaticSetI(Demo& d, int v)
{d.i = v;
}

我么可以看出,静态成员函数,没有直接调用变量i,而是通过对象来间接调用变量i,这说明什么呢?说明:静态成员函数不能访问普通成员变量(函数),需通过对象间接访问成员变量(函数)
下面做一个表格来对比一下静态成员函数与普通成员函数的区别:
在这里插入图片描述

好了,理解了静态成员函数,我们也应该来解决客户的需求了吧:直接上代码:

#include <stdio.h>class Test
{
private:static int cCount;
public:Test(){cCount++;}~Test(){--cCount;}static int GetCount(){return cCount;}
};int Test::cCount = 0;int main()
{printf("count = %d\n", Test::GetCount());Test t1;Test t2;printf("count = %d\n", t1.GetCount());printf("count = %d\n", t2.GetCount());Test* pt = new Test();printf("count = %d\n", pt->GetCount());delete pt;printf("count = %d\n", Test::GetCount());return 0;
}

运行结果为:

count = 0
count = 2
count = 2
count = 3
count = 2

分析: printf("count = %d\n", Test::GetCount());这段代码运行之前,没有创建对象,所以count = 0,

printf("count = %d\n", t1.GetCount());
printf("count = %d\n", t2.GetCount());

这两段代码运行之前已经创建了t1,t2这两个对象,所以都为:count = 2,

printf("count = %d\n", pt->GetCount());

这个运行之前又在堆空间创建了一个对象,并且让pt指针指向它,所以:count = 3

	delete pt;printf("count = %d\n", Test::GetCount());

在这两行先将pt指向的对象删除,所以最后是:count = 2

问题终于解决了!!!

总结:

  • 静态成员函数是类中的特殊的成员函数
  • 静态成员函数没有隐藏的this指针
  • 静态成员函数可以通过类名直接访问
  • 静态成员函数可以通过对象访问
  • 静态成员函数只能直接访问静态成员变量(函数),而不能直接访问普通成员变量(函数)

想获得各种学习资源以及交流学习的加我:
qq:1126137994
微信:liu1126137994
可以共同交流关于嵌入式,操作系统,C++语言,C语言,数据结构等技术问题!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/423355.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

eclipse 工程中使用引入maven项目遇到maven-resources-plugin:2.6 找不到

1.开始eclipse 配置本地库 首先是从maven 官网下载maven 组件 其次是配置 maven 环境和java 配置jdk 类似这里就不做介绍了 配置完环境后修改\apache-maven-3.3.9\conf\settings.xml 中的<localRepository> 设置本地仓库 然后配置eclipse 下面图中执行较为重要&#xff…

移植uboot之修改代码支持NorFlash记录

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 今天我们的任务是修改uboot源码支持NorFlash。 上两篇关于uboot移植的文章&#xff0c;我们修改了uboot源代码&#xff0c;支持了串口的输出&#xff…

移植uboot之修改代码支持NorFlash记录续集

接着上一篇文章写的内容&#xff08;上一篇文章链接&#xff1a;移植uboot之修改代码支持NORFLASH&#xff09;&#xff0c;上一篇结尾测试flash的擦除读写功能&#xff0c;结果无法写flash&#xff0c;卡在了这里&#xff1a; 前面已经擦除成功&#xff0c;这里写内容写不进…

C#开发终端式短信的原理和方法

本文示例源代码或素材下载 简介 没发过短信的年轻人肯定是属于那种受保护的稀有动物&#xff0c;通讯发达的今天短信已经成为人们交流的重要手段&#xff0c;其中也蕴含着巨大的市场和经济利益&#xff0c;掌握短信技术的人才也受到各大公司的追捧是目前职场上耀眼的明星。本文…

移植uboot之修改代码支持NorFlash记录续集二

先说一个事&#xff1a;我会在最后把移植好的uboot&#xff0c;内核&#xff0c;分别做一个补丁文件&#xff0c;以后如果用到相同的uboot以及内核都可以直接下载我这个补丁进行打补丁操作就可以直接用~ 上一个移植uboot续集&#xff0c;我们解决了无法写flash的问题&#xff…

Mysql function(函数)

1.mysql 拼接函数 1. 1CONCAT(string1,string2,…) 说明 : string1,string2代表字符串,concat函数在连接字符串的时候&#xff0c;只要其中一个是NULL,那么将返回NULL 1.2 CONCAT_WS(separator,str1,str2,...)说明 : string1,string2代表字符串,concat_ws 代表 conca…

【C++深度剖析教程6】C++之友元

这几天在复习数学考试&#xff0c;都没有学C&#xff0c;今天抽空来学一点。 什么是友元&#xff1f; 友元是C中的一种关系友元发生在函数与类之间或者类与类之间友元关系是单向的&#xff0c;不能传递 在具体讲解友元的性质之前&#xff0c;我们先来看看一个程序&#xff…

call stack and stack buffer overflow

http://en.wikipedia.org/wiki/Call_stack http://en.wikipedia.org/wiki/Stack_buffer_overflow Stack_buffer_overflow里提到的frame pointer 的位置不一样&#xff0c;不同的系统实现应该是不一样的。 运行时的栈是从高地址向低地址分配的&#xff0c;堆是从低地址向高地址…

【C++深度剖析教程8】C++的操作符重载的概念

之前学习了类的函数重载的概念&#xff0c;今天学习操作符重载的概念。在这之前我们先看一个例子&#xff1a; 上面是一个复数的加法&#xff0c;a为复数的实部&#xff0c;b为复数的虚部&#xff0c;在main函数里我想实现复数c1与c2的加法。很显然&#xff0c;正常的号操作符…

大数据开发者应该知道的分布式系统 CAP 理论

无论你是一个系统架构师&#xff0c;还是一个普通开发&#xff0c;当你开发或者设计一个分布式系统的时候&#xff0c;CAP理论是无论如何也绕不过去的。本文就来介绍一下到底什么是CAP理论&#xff0c;如何证明CAP理论&#xff0c;以及CAP的权衡问题。 CAP理论概述 CAP理论&a…

设计模式记--Observer Pattern观察者模式

观察者模式——定义了对象之间的一对多依赖&#xff0c;这样一来&#xff0c;当一个对像改变状态时&#xff0c;它的所有依赖者都会收到通知并自动更新. 从定义可以看出,OBSERVER(观察者)模式逻辑上需要两组对象来实现.首先它必需要有发布者(Publish),也可称为被观察的目标 (…

【C++深度剖析教程9】初探C++标准库

在这之前&#xff0c;我写的C程序不能叫做标准的C程序&#xff0c;因为里面写的大多数还带有C语言的影子。今天我们来学习C标准库。 首先看一下例子&#xff1a;操作符<<的原生意义是按位左移。那么我们重载这个操作符&#xff0c;将变量或者常量&#xff0c;左移到一个…

员工考核UI网页界面(PS大屏文件资料)

现分享人员管理可视化数据统计网页UI、员工考核数据可视化UI网页界面模版的UI源文件&#xff0c;供UI设计师们快速获取PSD源文件完成工作。 若需更多 大屏组件&#xff0c;请移步小7的另一篇文章&#xff1a;数据可视化大屏组件&#xff0c;大屏PSD设计源文件(大屏UI设计规范)…

docker 使用中遇到的问题

1.执行docker run hello 命令建立镜像过程中报错 开始以为是加速器的问题&#xff0c;将/etc/docker/daemon.json 文件删除还是不行 后来执行了这两条命令&#xff1a;就不报错了如下图&#xff1a; systemctl daemon-reloadsystemctl restart docker.service 参考&#xff1…

maven 解决冲突

1.Maven之jar包冲突解决&#xff08;理解maven 产生冲突的原因&#xff09; 导致jar包冲突的原因 1、mvn的传递依赖特性&#xff1a;mvn编译打包除了会引入直接申明的依赖&#xff0c;还会引入间接申明的依赖 2、mvn的依赖仲裁规则&#xff1a; 1&#xff09;. 按照项目总控P…

jz2440开发板修改UBOOT支持NAND FLASH

很多天没有看嵌入式的东西了&#xff0c;今天来看一下&#xff0c;继续之前移植uboot到jz2440开发板。今天我们来实现Uboot支持NAND FLASH。 在之前的文章里&#xff08;点击连接查看之前的记录&#xff09;&#xff0c;我们为了编译通过把NAND FLASH 给屏蔽掉了&#xff0c;现…

i.MX6网卡驱动程序fec.c的分析(AR8035网卡驱动程序的详细分析)之二

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 今天接着分析上次没有分析完的i.MX6网卡驱动程序。上一篇分析了iMX6网卡驱动程序的driver与device的加载过程&#xff08;点击可以查看上一篇文章&…

阅读ethercat官方文档关于ethercat网卡驱动程序的一些内容

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 改造iMX6&#xff08;fec&#xff09;网卡驱动程序前期工作之&#xff1a;阅读ethercat-1.5.2.pdf文档的第四章内容。 ethercat-1.5.2.pdf文档链接&a…

jz2440开发板移植U-boot之修改代码支持DM9000网卡

今天我们来移植U-boot到jz2440开发板&#xff0c;修改代码支持DM9000网卡。查看之前写的移植记录请点击链接&#xff1a;点击查看之前的移植记录 现在大多数开发板都支持DM9000网卡。我们的U-boot源码里面也是有DM9000网卡的驱动程序的。文件为Dm9000x.c&#xff08;drivers\n…

移植U-BOOT之裁剪和修改默认参数(易用性)启动内核,以及对uboot进行分区

今天我们来裁剪U-BOOT&#xff0c;使其更加易用&#xff0c;修改默认参数&#xff0c;以及制作最终修改好得补丁文件方便以后的快速移植。 那么如果想看之前的关于网卡以及flash等的移植&#xff0c;请点击链接查看&#xff1a;点击链接查看 在裁剪修改之前呢&#xff0c;我们…