类内静态函数和全局静态函数的相同点和不同点?
在函数或者变量前面加上static修饰符号,以便把函数或者变量在类内或者文件范围内共享,那么我们把这种函数和变量叫静态函数和静态变量。
我们把函数和变量声明为静态的有什么优点那,从静态变量的角度看,更容易理解一些
使用静态数据成员可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,就可以保证所有对象都能够访问到被更新后的值,这样可以提高效率和节省内存空间。这是静态变量的优点同时也是他的一个缺点,在类中声明了静态变量,无论用户使用使用这个类,而这个静态变量都会申请他需要的内存容量。对于多线程的情况下,访问静态变量我们需要加一些异步机制,防止多个线程同时修改静态变量。
类内静态函数比全局静态函数的用处要多的多,在这里先讲简单的
全局静态函数的应用比较常见的就是
static int fun()
{
...;
return 1;
}
当我们希望在多个类中调用fun函数时,我们必须把fun声明为static类型,不然在link时编译器会发现多个关于fun的定义。这种函数的应用,多少带有C的色彩,尤其当我们在C环境写好的函数,移植到C++中时,需要在函数前面需要加上static,而如果我们需要移植多个函数时,更通用的一种方法是使用未命名名字空间
namespace{
int fun()
{
...;
return 1;
}
…
}
也许有人问如果在未命名名字空间中的函数再加上static修饰符号会怎么样?这个就完全取决以使用的编译器,可能会直接报错,也可能可以编译并正常使用。不过这样写,代码从语言层次上就很难移植编译环境,所以尽量不要这样写。
关于这样的应用,在我从前的项目中实施过。当时我需要定义很多函数,执行三角形,像素转换方面的计算,所以把这些函数声明为static的。
对于与全局静态函数,我们继续类内静态函数和静态变量的讨论,我想他们的应用是两者区别的最好体现。
对于类内静态变量,我们需要在类外初始化,这是在使用静态变量需要注意的地方,有些初学者很容易在这里出错。类内静态变量使用最多的就是计算类的实例化个数。
class A
{
static int i; // 外部初始化为0
public:
A() // 构造函数
{
++i;
}
~A() // 析构函数
{
--i;
}
static int CreateObjectNumber() // 或者创建对象的数目
{
return i;
}
};
也许读者认为这种方法,只是用来学习而已,实际很少使用。我不赞同这个观点。
在程序设计中,比如当前我做的皮肤系统中,我需要对所有创建的对象进行,并统计创建和销毁的对象是否相等,从而判断是否存在泄漏问题。所以我定义这样的一个类
// 由于版权,我只保留说明问题的部分
class SkinBaseImpl
{
public:
SkinBaseImpl(void);
virtual ~SkinBaseImpl(void);
public:
…
#ifdef SKINTRACE
private:
static int _ObjNum;
#endif
};
#ifdef SKINTRACE
int SkinBaseImpl::_ObjNum = 0;
#endif
/*=======================================================================*/
/**
* @bri: SkinBaseImpl构造函数
* @param: void
*/
SkinBaseImpl::SkinBaseImpl(void):_useSkin(USE)
{
#ifdef SKINTRACE
TRACE( "Create new object, the number is %d/n ",++_ObjNum);
#endif
}
/*=======================================================================*/
/**
* @bri: SkinBaseImpl析构函数
* @param: void
*/
SkinBaseImpl::~SkinBaseImpl(void)
{
#ifdef SKINTRACE
TRACE( "free the %d object/n ",_ObjNum--);
#endif
}
其他需要管理和统计创建销毁对象的类就可以从这个类继承。
在这里我定义了宏SKINTRACE,当需要使用trace,以便获取创建对象的数量时,定义这个宏就可以。而不需要时,比如发布版时,只需注销到SKINTRACE的定义,这样不会在发布颁布留下任何痕迹。
通过上面的例子,我们知道静态函数和静态变量的几个特性
1:静态变量受public,protected ,private限制,也就是如果静态变量是protected或者private类型的,在类外不能访问,比如
A::i是错误的
这条规则同样适用于静态函数
2:静态变量在类内声明,而必须在类外初始化,模版类中应用也是这样。这里我们在static后面加上const类型,可以直接初始化。比如
Class A
{
// Static int I = 5; // error
Static const int I = 5; // ok
Int m_list[I];
}
而这里I的应用也无非是Int m_list[I];
3:静态成员函数只能访问类的静态变量,而类的成员函数也可以访问类的静态变量,这样就可以通过静态成员变量建立类的静态成员函数和类对象的关联关系。
4:还存在一种静态变量,他不是全局静态变量,而是函数内的静态变量,如下例中的i,这算是对全局静态变量的一种补充。
int fun()
{
static int i = 3;
++i;
return i;
}
这种方式的好处时,只用调用fun函数时,静态变量i才申请内存,这也符合lazy evaluation的设计要求。只有当需要时,才去申请。
同样作为破坏封装的一种技术应用是友元函数或者友元类的应用,很多人形象比喻这种方式是在封装的物体上开了一个小小的洞,不提倡使用这种技术。其实任何技术都有他应用的场所,不然就不会出现这种技术。不过不去了解这种特性,也许永远我们不会知道这些技术的重要性。碰见这些技术也只会使用有色眼镜去看。友元函数的特征基本如下
1) 必须在类的说明中说明友元函数,说明时以关键字friend开头,后跟友元函数的函数原型,友元函数的说明可以出现在类的任何地方,包括在private和public部分,不受private限制
2) 友元函数不是类的成员函数,所以友元函数的实现和普通函数一样,在实现时不用 ":: "指示属于哪个类,只有成员函数才使用 ":: "作用域符号;
3) 友元函数不能直接访问类的成员,只能访问对象成员,所以在调用友元函数时,确保友元类的必须实例化。
4) 友元函数可以访问对象的私有成员,但普通函数不行,这个需要注意,尤其是在友元类中,有时候发现两个类互相为友元类,确不能调用成员函数,就是这个原因。
5) 调用友元函数时,在实际参数中需要指出要访问的对象,也可以把对象声明为全局对象而在友元函数中调用,当然在友元函数中可以调用其他全局函数,或者实例对象等操作。
使用友员函数最大的优点就是,不用对类中的每个变量写Get/Set接口函数。尤其是当类中有大量的私有成员变量,而又不想为每个变量设置接口,同时又需要外部的某个函数调用。这样最好就是把这个函数声明为友元函数,我们在一些开源项目中很常见这种技术,比如阿agentx++。
说道现在我一直没有提到模版中静态函数的应用,其实对于模版的应用,我不是很熟练。只能简单的说明一下.
class AU
{
public:
AU(){
};
string GetAU()
{
return "Base--GetAU ";
}
virtual string GetAUU()
{
return "Base--GetAUU ";
};
virtual ~AU(){};
};
template <class T,class TBase>
class TEMU:public TBase
{
public:
string GetAA()
{
T* pt = static_cast <T*> (this);
return pt-> GetA(); // 这里调用的是static string GetA()函数
}
string GetBB()
{
T* pt = static_cast <T*> (this);
return pt-> GetB(); // 这里调用的是string GetB()
}
public:
string GetA()
{
return "TEMU - GetA ";
}
string GetB()
{
return "TEMU - GetB ";
}
};
class DeriveTEMU : public TEMU <DeriveTEMU,AU>
{
public:
static string GetA() // 注意这里是静态函数
{
return "DeriveTEMU - GetA ";
}
string GetB()
{
return "DeriveTEMU - GetB ";
}
};
测试用力
DeriveTEMU u;
TEMU <DeriveTEMU,AU> *p = &u;
cout < < p-> GetAA() < < endl;
cout < < p-> GetBB() < < endl;
输出结果
DeriveTEMU - GetA
DeriveTEMU – GetB
在这里我们看到,调用类内静态函数的方式并不是简单的类名::函数的形式,而是通过模版父类调用子类静态函数,同样也给出了,调用普通函数的方式。这种机制可以理解为模版继承关系中的虚继承关系。当认识到模版中的静态函数使用,也许会更大的改变我们对静态函数的印象,这种机制在ATL,WTL中有广泛的应用,几乎每种涉及到消息影射关系的类中,都使用这种方式。
以上是我对静态函数,静态变量的简单认识。也没有面面俱到的讲道静态的细节,这样说估计很多人会反感的。如果有错误,不妥,或者您认为重要但没有提到的,欢迎指正。
本回复公布一周,一周后揭帖,并发布下一个议题,同时欢迎大家公布自己的议题。
http://topic.csdn.net/t/20061023/16/5102847.html
类内静态函数和全局静态函数的相同点和不同点?相关推荐
- 线性判别分析(Linear Discriminant Analysis, LDA)(含类内散度矩阵 类间散度矩阵 全局散度矩阵推导
LDA算法概述: 线性判别式分析(Linear Discriminant Analysis, LDA),也叫做Fisher线性判别(Fisher Linear Discriminant ,FLD),是 ...
- 静态函数和非静态函数的区别(静态方法和非静态方法)
首先,静态函数只有当程序结束的时候才从内存消失,生命周期长.而非静态则是动态加载到内存,不需要的时候就从内存消失. 而调用类中的静态函数,无需创建对象就可以调用了,因为当类初始化的时候,就已经加载了静 ...
- C++ 学习 ::【基础篇:17】:C++ 类与对象:运算符重载介绍、运算符重载函数(类内与类外区别)写法及简单设计实现
本系列 C++ 相关文章 仅为笔者学习笔记记录,用自己的理解记录学习!C++ 学习系列将分为三个阶段:基础篇.STL 篇.高阶数据结构与算法篇,相关重点内容如下: 基础篇:类与对象(涉及C++的三大特 ...
- c++类内的static变量初始化和static函数
class A {static int si; static void sfunc(){cout<< si;} }; int A::si=0;//静态成员不能在类内初始化. 使用时:int ...
- JS(内置对象,全局函数,事件,事件对象)
目录 内置对象 全局函数 事件 事件对象 内置对象 <!DOCTYPE html> <html><head><meta charset="utf-8 ...
- C++编程进阶6(public继承与组合、private继承、多重继承、处理模板基类内的名称、如何避免模板代码膨胀)
二十一.public继承与组合 public继承是是子类对象is a基类对象的关系,比如QT中的所有组件类都要继承QObject,所以所有的QT组件都是一个QObject. 而组合是has a(包含) ...
- C++编程进阶2(编译器在类内默认生成的函数讨论以及纯虚析构函数)
三.编译器默认提供的类内函数讨论 1.当写下一个空类时,编译器会在必要的时候默认提供四个函数:构造.拷贝构造.operator=和析构函数,而且都是public的 class Empty{ }; 上述 ...
- c++学习笔记之基础---类内声明函数后在类外定义的一种方法
在C++的"类"中经常遇到这样的函数, 返回值类型名 类名::函数成员名(参数表){ 函数体.} 双冒号的作用 ::域名解析符!返回值类型名 类名::函数成员名(参数表) { 函数 ...
- boost::regex模块在 cpp 文件中搜索类定义,使用全局数据的测试程序
boost::regex模块在 cpp 文件中搜索类定义,使用全局数据的测试程序 实现功能 C++实现代码 实现功能 boost::regex模块在 cpp 文件中搜索类定义,使用全局数据的测试程序 ...
- boost::regex模块在 cpp 文件中搜索类定义,使用全局回调函数的测试程序
boost::regex模块在 cpp 文件中搜索类定义,使用全局回调函数的测试程序 实现功能 C++实现代码 实现功能 boost::regex模块在 cpp 文件中搜索类定义,使用全局回调函数的测 ...
最新文章
- 实施自动化测试的六个目标和意义
- Redis架构第二天:CenterOS集群、RDB和AOF、主从复制架构实践
- 轻量级分布式文件系统FastDFS使用安装说明手册(新手入门级)
- linux c http下载 带确认 进度条
- TensorFlow学习笔记(二十八)CNN的9大模型之AlexNet
- nodejs实践录:简单的log日志模块
- 主机连接虚拟机 web服务
- http://www.uupoop.com/ps/
- paip.mysql 性能测试 报告 home right
- 精心梳理二十二道常见SSM面试题(带答案)
- 期货开户公司想恶意滑点是做不到的
- 当英文遇上汉语 就知道汉语有多强大了
- MEDA: Meta-Learning with Data Augmentation for Few-Shot Text Classification
- 【计算机图形学】基于OpenGL的中点Bresenham算法画直线
- 通达信指标函数说明大全(2014)(转)
- 区块链技术研究热点有哪些
- linux usleep占用cpu,[RK_2014_0918]linux下,测试usleep函数对CPU占用率的影响
- 【环境部署系列 06】Ascend 310(推理)X86服务器 Ubuntu系统环境部署
- adams功能区不显示_技巧 | Word 文本突出显示颜色,原来还隐藏了一种颜色
- 解决win10任务栏软件图标变成白色的问题
热门文章
- JB的Python之旅-爬虫篇-MM图
- wordpress 静态化_品牌化WordPress网站的10个基本步骤
- 基于《十三机兵防卫圈》的音频整合设计理念及工程分享
- 再见,OI ---GDOI2017爆零记
- 植物大战僵尸开外挂,不如自己做一个玩的更开心
- 外企HR经常提问的问题
- mysql静态视图_在Django中创建第一个静态视图
- FPT Software的数字解决方案在2020年国际商务大奖(R)评选中名列前茅
- C语言学习_First_day(初识C语言,visult studio的使用)
- java+ascii+汉字编码_ASCII码,中文汉字与Uincode编码相互转换在线计算器_三贝计算网_23bei.com...