对于对象(或变量)使用static修饰,表明了两点信息:
一是该变量在静态存储存创建,main结束时它的生命才结束。
二是该变量最多只被初始化一次(可能一次也没有)。
{
static const char * pStr; // const意味pStr指向的内容不能改变。static意味着在静态数据区存放这个变量,而且在这个地方,编译器会加上把pStr初始化的语句即把pStr置为0,而且这个初始化动作只会在程序运行到这里时执行一次,以后即使再运行到这里也不再执行初始化的操作。
...
}
对于内置类型的static变量,如果没有初始化赋值(如static const char * pStr = 0;),则编译器会自动加上合适的0值。编译的时候只是在静态存储区分配内存,但并不初始化这个内存--初始化这个内存是在程序第一次运行到static变量定义的地方而进行的。
对于自定义类型的static对象,编译器要插入相应的构造函数来对这个static对象进行初始化,当然也是在程序第一次运行到定义处才进行。
class Y
{
int i;
public:
Y( int n ) : i(n) {}
~Y( ) {}
};
void testStatic( )
{
static Y y1(10); // 编译器在这里插入构造函数但只在第一次运行到这里时才执行
//static Y y2; //error,已经提供了构造函数则编译不再提供默认的构造函数,即没有无参的构造函数了
}
全局的对象或变量也在静态存储区创建,而且,它在进入main函数之前就创建了--如果它有构造函数则在进入main之前就执行了构造函数。
class obj
{
char c;
public:
obj( char C ) : c(C) { cout << c << endl;}
~obj( ) { cout << "~" << c << endl; }
};
obj A('a'); // 在进入main之前这个对象的构造函数就被调用了
void f( )
{
static obj B('b');
static obj C('c'); // 在出main函数时C先于B销毁,因为static的销毁顺序与创建相反
}
void g( )
{
static obj D('d');
}
void main( )
{
cout << "in main" << endl;
f( );
//g( ); // D对象分配了内存,但没有初始化的机会,因为函数没有被调用
cout << "leave main" << endl;
}
输出:
a
in main
b
c
leave main
~c
~b
~a
对于类里面声明的变量,或函数里面的局部变量,它们是没有连接的,所以如果给它们加static则表示它们要在静态存储区里面分配空间,而跟它们的可见性没有任何关系。对这些变量加extern是没有任何意义的。
对于文件范围内的变量(非类里面声明的,非函数里面定义的),它们本身就是在静态存储区分配的空间的,如果给它们加上static,则是为了改变它们的可见性,即加了static后它们就只在它们的编译单元(.cpp文件)里面可见,而对于别的编译文件不可见(即别的文件用这个名字不会出现冲突)。如果加上extern或不加,则表示这些变量是全局可见的(即所有的编译单元都可见)。
对于函数,加或不加extern表明这个函数是全局可见的;加static则表明这个函数只对它自己的编译单元可见。
名字空间的引入是为了避免名字冲突。
namespace thisNamespaceIsSoLong
{
int n = 10;
// ...
}
namespace TN = thisNamespaceIsSoLong; // 起个别名
cout << TN::n << endl;
类的静态数据成员在静态存储区里面分配内存,而且这个类的所有对象共享这一个内存(只有一份)。
这些静态的数据成员在类里面声明,然后必须在类的外面(如cpp文件里)进行定义(只定义一次):
class A {
static int i; // static数据成员声明
public:
//...
};
在cpp文件里面:
int A::i = 10; // 在这里定义,仅一次
///////// 静态数据成员的初始化表达式是在类的范围内
int x = 100;
class withStatic
{
static int x;
static int y;
public:
void print( ) const
{
cout << "withStatic::x == " << x << endl;
cout << "withStatic::y == " << y << endl;
}
};
int withStatic::x = 1;
int withStatic::y = x + 1; //静态数据成员的初始化表达式是在类的范围内,所以这里的x是指类里面的x。
void testStatic( )
{
withStatic ws;
ws.print( ); // 输出1, 2
}
//////// 类静态数组的初如化:
class Values
{
static const int size;
static const float talbe[4];
static char letter[3];
};
const int Values::size = 100;
const float Values::talbe[4] = {1.2f, 2.3f, 3.4f, 4.5f};
char Values::letter[3] = {'a', 'b', 'c'};
//////////////// 代替enum的做法
class X {
static const int size; // enum {size = 100};
int array[size]; // enum里面只能是整数
public:
// ...
};
const int size = 100;
嵌套类里面可以使用static数据成员,但函数里面的局部类不能使用static数据成员。
static成员函数没有this指针,所以它不能访问非static的数据成员,也不调用非static的成员函数。
static成员函数服务于类而不是具体的对象,可以由对象或类名调用。
注意,非static的成员函数是可以访问static数据成员和调用static成员函数的。
///////// 一个只有一个对象的类:
class egg
{
static egg E; // 类里面的static对象,这是这个类的唯一对象
int i;
egg( int n = 0 ) : i(n) {}; // 构造函数是private的,所以不能再产生对象
public:
static egg* Instance( ) { return &E; } // static函数访问static数据
int value( ) const { return i; }
};
egg egg::E( 100 ); // 定义这个唯一的对象
// 测试代码:
void testStatic2( )
{
//egg e; // error,private的构造函数
cout << egg::Instance()->value( ) << endl;;
}
extern "C" float f(char a, int b);
或
extern "C"
{
float f(char a, int b);
void g(double d );
}
表示"C"后面的函数是使用C连接的,所以C++编译器不会再做名字的转换--比如它可能转换出这样的名字:_f_char_int,现在加了extern "C"后,就保留原有的名字_f。在使用C库时,这种方法能够解决连接的问题。