类内静态函数和全局静态函数的相同点和不同点

 

1)全局静态函数只在本编译单元有效。
2)类静态函数,对比普通成员函数,没有this指针,不能访问非static成员。
3)要说二者相同点,都是c++名字控制的手段:全局静态函数被局限在一个编译单元,通俗的将就是在一个cpp文件中定义的全局静态函数在另一个cpp文件中不能被调用。例程如下;

//   main.cpp
#include   <iostream>

extern   int   hello2();

static   int   hello()
{
        std::cout   < <   "hello,   world! "   < <   std::endl;
        return   0;
}

int   main()
{
        hello();
        hello2();
}

//   append.cpp
extern   int   hello();

int   hello2()
{
        hello();
}
//---------------------------------------------
编译将产生错误:
append.obj   :   error   LNK2001:   unresolved   external   symbol   "int   __cdecl   hello(void) "   (?hello@@YAHXZ)
Debug/Test.exe   :   fatal   error   LNK1120:   1   unresolved   externals

而把hello前面的static去掉之后就可以编译成功了。

可见不能在append.cpp中调用main.cpp中的全局静态成员函数。 

而类静态函数,必须使用其“类名::静态函数名”

#include<iostream>
using namespace std;
class A
{
	public:
	static void fun(int x)
	{
		
		cout<<x<<endl;
	}
};
int main()
{
	A son;
	son.fun(5);
//	A::fun(5);
	return 0;
}


编译虽然会出结果,但是有一条警告,son未引用的局部变量,说明静态成员函数不可以用对象调用的。但是至于为什么会输出正确结果不太理解,望高手指教。也有待进一步研究。用//注释起来的也可以输出正确结果。

我们把函数和变量声明为静态的有什么优点那,从静态变量的角度看,更容易理解一些
使用静态数据成员可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,就可以保证所有对象都能够访问到被更新后的值,这样可以提高效率和节省内存空间。这是静态变量的优点同时也是他的一个缺点,在类中声明了静态变量,无论用户使用使用这个类,而这个静态变量都会申请他需要的内存容量。
对于多线程的情况下,访问静态变量我们需要加一些异步机制,防止多个线程同时修改静态变量。
类内静态函数比全局静态函数的用处要多的多,在这里先讲简单的
全局静态函数的应用比较常见的就是
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;
}
};

也许读者认为这种方法,只是用来学习而已,实际很少使用。我不赞同这个观点。
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中有广泛的应用,几乎每种涉及到消息影射关系的类中,都使用这种方式。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值