0%

C++类的静态成员和C语言中的static

一、C++类的静态成员

学过C++的都知道,静态成员是属于整个类而不是某个对象的,静态成员变量在内存中只存储一份共所有对象共用,在所有对象中都可以共享它。

静态成员的定义或声明需要加个关键字 static。因为属于整个类,所以可以通过 <类名>::<静态成员名>来使用。

比较容易混淆和出错的地方在于类的静态成员变量和静态成员函数的使用,这两者都属于静态成员。

需要注意的是:静态成员属于整个类,在类对象实例化前,静态成员就已经分配空间了,而非静态成员无需初始化,必须在类实例化对象后才有内存空间,所以就有了个先后顺序:静态成员先于非静态成员存在。

下面我们来看看如下几个情况:

1、通过类名调用静态成员函数和非静态成员函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Base
{
public:
void fun1()
{
}
static void fun2()
{
}
};
int main()
{
Base::fun1();
Base::fun2();
return 0;
}

编译错误:“Base::fun1”: 非静态成员函数的非法调用
结论1:不能通过类名来调用类的非静态成员函数。
原因:类没有实例化对象,非静态成员函数不存在

2、通过类的对象静态成员函数和非静态成员函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Base
{
public:
void fun1()
{
}
static void fun2()
{
}
};
int main()
{
Base obj;
obj.fun1();
obj.fun2();
return 0;
}

编译通过
结论2:可以通过类的对象调用静态成员函数和非静态成员函数
原因:类实例化对象,非静态成员函数已分配内存,静态成员函数为类各对象共享

3、在类的静态成员函数中使用类的非静态成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Base
{
public:
void fun1()
{
}
static void fun2()
{
cout << x << endl;
}
private:
int x;
};
int main()
{
Base obj;
obj.fun2();
return 0;
}

编译出错:对非静态成员“Base::x”的非法引用
结论3:类的静态成员函数不能使用类的非静态成员
原因:在编译阶段,静态成员函数已分配内存,但非静态成员并没有分配内存,就好比提前使用一个不存在的东西

4、在类的非静态成员函数中使用类的静态成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Base
{
public:
void fun1()
{
fun2();
}
static void fun2()
{
}
};
int main()
{
Base obj;
obj.fun1();
return 0;
}

编译通过
结论4:类的非静态成员函数可以调用静态成员,反之则不能
理由同前

5、使用类的静态成员变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Base
{
public:
void fun1()
{
}
static void fun2()
{
}
static int x;
};
//int Base::x = 0;
int main()
{
cout << Base::x << endl;
return 0;
}

链接出错:无法解析的外部符号 “private: static int Base::x” (?x@Base@@0HA)
结论5:类的静态成员变量必须初始化,并且静态成员变量的初始化必须在类外初始化,且不能带static关键字
原因:静态成员变量属于类作用域,不属于类对象,程序一运行就分配内存并初始化,生命周期和程序一致,在类的构造函数内初始化显然是不合理的。另外在类外初始化时,还必须指定类型,否则编译器不知道该为变量分配多大内存。

但是,静态成员也并非不能再类中初始化。对于静态常量成员是可以在类中初始化的。

总结:
1.静态成员存在于内存,非静态成员需要实例化才会分配内存
2.非静态成员的生存期取决于该类的生存期,而静态成员则不存在生存期的概念
3.类的静态成员函数不能调用非静态成员,存在的先后顺序,反之可以
4.类的静态成员变量必须初始化,且必须在类外初始化(静态常量成员除外),不能带static关键字,必须带类型。

二、C语言中的static

下面介绍在C语言中,static的作用:

1、隐藏

当同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。比如,同时编译两个源文件,一个是a.c,另一个是main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//a.c
char a = 'A';
void msg()
{
printf("Hello\n");
}
//main.c
int main()
{
extern char a;
printf("%c", a);
(void)msg();
return 0;
}
//output:A Hello

由于a.c 中的所有成员(变量和函数)都未加static,所有都具有全局可见性,其他的源文件也能访问。如果加了static,那么其余源文件就不能访问了,利用这一特性,可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。

2、static还能保持变量内容的持久

对于函数而言,static的作用仅限于隐藏,对于变量,static还有两个作用,其一就是保持变量内容的持久。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化,事实上,全局变量和静态变量都存储在静态存储区。与全局变量不同的是,static可以控制变量的可见范围。

1
2
3
4
5
int fun()
{
static int count = 10;//事实上此赋值语句从来没执行过,调试一进入函数,count=10
return count--;
}

3、static第三个作用则是默认初始化为0,事实上存储在静态存储区的变量都具有这样的特性,所有全局变量也能初始化为0.