C++编程思想学习笔记-命名控制
一、 static的使用
1、 对于全局变量(即main函数之外的变量):static表示内部连接,无staic则为外部连接,而用extern可以使用其他编译单元中的外部连接变量。另外全局变量始终存储在静态存储区。全局静态对象的构造函数是在main()之前执行的,而析构函数是在main()之后执行的,所以利用这点可以在main()函数之前之后执行代码。一般头文件中的变量应控制为内部连接,这样两个同时都包含此头文件的编译单元在连接时就不会有名字冲突了。
2、 对于局部变量(分配于堆栈的变量):static使变量被分配到静态存储区,默认初始化一次且第一次(内部数据类型为0,用户自定义调用构造函数),生命期同全局变量(从定义到程序结束),但只在该函数内部可见。静态对象的析构函数在main函数退出时调用,多数是随着exit()函数的调用而被调用,所以不要在析构函数中调用exit()函数,以防死循环。但若用c库函数abort()结束程序则析构函数不会被调用。静态对象的销毁是按它们初始化的相反顺序进行的,且未被调用函数中的静态对象不会被创建,所以也不会被销毁。
3、 对于类中的成员:static使类的静态成员拥有一个单独的静态存储区,这一存储区属于该类,该类的对象共享这块存储区,共享这些静态成员,这样就提供了一种让对象之间通信的方法。静态成员属于类,地址是定的;而非静态成员属于对象,地址是动态分配的;所以静态成员只能调用静态成员,不能调用非静态成员;而非静态成员则可以都访问,但对静态成员的访问会影响类的数据及其他的对象。
×静态数据成员只能在类的外部定义且只能定义一次,一般放在类的实现文件中;
注意:在定义静态数组时不能自动计数,必须指明数组大小。另外static const int可以做为类中的编译时常量。在嵌套类中可以定义静态成员,但在局部类中不能。
×静态成员函数只能访问静态数据成员和静态成员函数,这有一个应用,可以定义一个只能有一个对象的类,首先构造函数必须为私有(保证不能构造其他实例),但如何初始化呢?所以这个对象必须是类中的静态数据成员(保证可以用静态成员的定义方法来初始化),示例如下:
#include<iostream.h>
Class egg{
static egg E;//为什么要私有?
int i;//此数据属于对象,必须用对象访问
egg(int I):i(I){}//构造函数私有,只能内部访问。
public:
static egg* instance(){return &E;}//获得对象的地址
int val(){return i;}
};
egg egg::E(47);
main(){
//! egg x(1); //不能创建对象;
cout<<egg::instance()->val()<<endl;
}
4、 静态初始化的依赖(extern的使用):看如下示例:
×示例1
//first file
#include<fstream.h>
ostream out(“out.txt”);
第二个文件
//second file
#include<fstream.h>
extern ofstream out;
class oof{
public:
oof(){out<<”barf”;}
}OOF;
这个示例程序可能运行,也可能不运行,若建立可执行文件时第一个文件先初始化则没问题,但若第二个文件先初始化,则会引起混乱。
×示例2(ARM中)
第一个文件
extern int y;
int x=y+1;
第二个文件
extern int x;
int y=x+1;
对于这个示例,若第一个文件先初始化则y=0,x=1,y=2;若第二个文件先初始化则x=0,y=1,x=2;这两种结果相差很大。
×解决方法:
(1)不用它;(2)关键的静态对象的定义放到一个文件中;
(3)在库头文件中增加一个额外的类控制库中静态对象的动态初始化,示例如下:
#ifdef DEFEND_H_
#define DEFEND_H_
#include <iostream>
extern int x;//Declarations,not definitions
extern int y;
class initializer
{
static int init_conut;
public:
initializer()
{
cout<<"initializer()"<<endl;
//Initialize first time only
if(init_conut++==0)
{
cout<<"performing initialization"<<endl;
x=100;
y=200;
}
}
~initializer()
{
cout<<"~initializer()"<<endl;
//Clean up last time only
if(--init_conut==0)
{
cout<<"performing cleanup"<<endl;
//Any necessary cleanup here
}
}
protected:
private:
};
//the following creates one object in each
//file where DEFEND.H is included,but that
//object is only visible within that file:
static initializer init;
#endif
实现文件如下:
#include "depend.h"
//static initialization will force
//all these values to zero;
int x;
int y;
int initializer::init_conut;
此种情况是在你的库中有extern对象,会引起初始化依赖问题,通过一个类中的静态计数变量来来记录初始化,若是第一次包含头文件则初始化,否则不用再初始化,对于其他的头文件包含则会跳过初始化;即保证任何情况都会初始化且只一次。
(extern “C”{ func1(a,b);func2(a,b);}的使用方法);
二、namespace的使用
1、namespace只能在全局范畴定义,但可以嵌套;
2、namespace可以在多个头文件中用同一个标识符来定义;
3、一个namespace可以用另外一个名字做为别名:namespace a=abcdef;
4、未命名的namespace表示全部是内部连接;
5、可以在namespace中插入friend声明,则被声明的友元也成为此namespace
中的一员;
6、using namespace a;
using namespace b;
当a和b中存在相同的名字时,在使用时会产生冲突;
而利用using声明可以覆盖掉using指令中的名字;
using指令是省去了每次输入名字空间修饰的麻烦,而using声明是把名
字空间中的成员再次声明一下;举例如下:
namespace U{
void f();
void g();
}
namespace V{
void f();
void g();
}
对于using namespace U;f();等同于U::f();此时f为全局名,可被局部名覆盖;
对于using U::f;f();等同于void f();f();//U中f的再次声明
此时f为局部名;效果如下面代码:
void func(){
using namespace U;//Using directive
using V::f;//Using declaration
f();//calls V::f()
U::f();//must fully qualify to call
}