Static静态类型和this指针

Static静态类型详解

1):修饰全局变量时,表明一个全局变量只对定义在同一文件中的函数可见。

2):修饰局部变量时,表明该变量的值不会因为函数终止而丢失。

3):修饰函数时,表明该函数只在同一文件中调用。(这里指的同一文件不包括头文件包含的方式)

c++独有:

4):修饰类的数据成员,表明对该类所有对象这个数据成员都只有一个实例。即该实例归 所有对象共有。

5):用static修饰不访问非静态数据成员的类成员函数。这意味着一个静态成员函数只能访问它的参数、类的静态数据成员和全局变量

单指C中static

1):1. 静态局部变量:用于函数体内部修饰变量,这种变量的生存期长于该函数。(static放在全局区,function放在栈区,函数结束时收回资源)

1
2
3
4
5
6
int foo(){
static int i = 1; // note:1
//int i = 1; // note:2
i += 1;
return i;
}

2.静态全局变量:定义在函数体外,用于修饰全局变量,表示该变量只在本文件可见。

1
2
3
4
5
6
7
#inndef Code_h
#include "stdio.h"
int i = 0;
void test(void){
i++;
}
#endif
1
2
3
4
5
6
7
#include "Code.h"
int i = 0;
//报错,变量重复定义。如果Code_h中定义的是static int i = 0;则编译通过
//因为static关键字修饰是止作用与本类。
int main(){
i++;//如果使用的code.h中使用的是static关键字定义的i;此处i++ = 1;
}

静态全局变量不能被其它文件所用(全局变量可以);
其它文件中可以定义相同名字的变量,不会发生冲突(自然了,因为static隔离了文件,其它文件使用相同的名字的变量,也跟它没关系了);

3):静态函数:静态函数类似于静态全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
//file a.c
#include <stdio.h>
void fn()
{
printf("this is non-static func in a");
}
//file b.c
#include <stdio.h>
extern void fn(); //我们用extern声明其他文件的fn(),供本文件使用。
void main()
{
fn();
}

可以正常输出:this is non-static func in a。
当给void fn()加上static的关键字之后呢? undefined reference to “fn”.

所以,静态函数的好处跟静态全局变量的好处就类似了:
1.静态函数不能被其它文件所用;
2.其它文件中可以定义相同名字的函数,不会发生冲突;

上面一共说了三种用法,为什么说准确来说是两种呢?
1.一种是修饰变量,一种是修饰函数,所以说是两种(这种解释不多)。
2.静态全局变量和修饰静态函数的作用是一样的,一般合并为一种。(这是比较多的分法)。

C++中static的扩展用法(C中用法同样支持)

1.静态数据成员:用于修饰 class 的数据成员,即所谓“静态成员”。这种数据成员的生存期大于 class 的对象(实体 instance)。静态数据成员是每个 class 有一份,普通数据成员是每个 instance 有一份,因此静态数据成员也叫做类变量,而普通数据成员也叫做实例变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Static_cpp{
public:
static int num;
//static 类型在类中声明时不能同时赋值,必须使用int Static_cpp :: num = value的方式赋值

};

int Static_cpp ::num = 10;

void Staticpp_test(){
cout<<Static_cpp::num<<endl;
}

2.静态成员函数:用于修饰 class 的成员函数。

我们对上述例子稍加修改得到如下例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int N = 10;
class Static_cpp{
public:
static int num;
//static 类型在类中声明时不能同时赋值,必须使用int Static_cpp :: num = value的方式赋值
static void Static_funciton(){
cout<<"the stactic num value = "<<num<<endl;
// cout<<"the nums value = "<<nums<<endl;
//报错Invalid use of member 'nums' in static member function
//静态函数内部不能调用非全局变量
cout<<"the N value = "<<N<<endl;//output = 10;
}
};

int Static_cpp ::num = 10;

void Staticpp_test(){
cout<<Static_cpp::num<<endl;
Static_cpp::Static_funciton();
}
/*10
the stactic num value = 10
0*/

那么静态成员函数有特点呢?
1.静态成员之间可以相互访问,包括静态成员函数访问静态数据成员和访问静态成员函数;
2.非静态成员函数可以任意地访问静态成员函数和静态数据成员;
3.静态成员函数不能访问非静态成员函数和非静态数据成员;
4.调用静态成员函数,可以用成员访问操作符(.)和(->)为一个类的对象或指向类对象的指针调用静态成员函数,也可以用类名::函数名调用(因为他本来就是属于类的,用类名调用很正常)

this指针

C++ 程序到C程序的翻译

C++ 是在C语言的基础上发展而来的,第一个 C++ 的编译器实际上是将 C++ 程序翻译成C语言程序,然后再用C语言编译器进行编译。

C语言没有类的概念,只有结构,函数都是全局函数,没有成员函数。翻译时,将 class 翻译成 struct、对象翻译成结构变量是显而易见的,但是对类的成员函数应该如何翻译?对myCar.Modify();这样通过一个对象调用成员函数的语句,又该如何翻译呢?

C语言中只有全局函数,因此成员函数只能被翻译成全局函数;myCar.Modify();这样的语句也只能被翻译成普通的调用全局函数的语句。那如何让翻译后的 Modify 全局函数还能作用在 myCar 这个结构变量上呢?答案就是引入“this 指针”。下面来看一段 C++ 程序到C 程序的翻译。

C++程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class CCar
{
public:
int price;
void SetPrice(int p);
};

void CCar::SetPrice(int p)
{
price= p;
}
int main()
{
CCar car;
car.SetPrice(20000);
return 0;
}

翻译后的C程序(此程序应保存为扩展名为 .c 的文件后再编译):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct CCar
{
int price;
};
void SetPrice(struct CCar* this, int p)
{
this->price = p;
}
int main()
{
struct CCar car;
SetPrice(&car, 20000);
return 0;
}

可以看出,类被翻译成结构体,对象被翻译成结构变量,成员函数被翻译成全局函数。但是C程序的全局函数 SetPrice 比 C++ 的成员函数 SelPrice 多了一个参数,就是struct CCar *thiscar.SetPrice(20000);被翻译成SetPrice(&car, 20000);,后者在执行时,this 形参指向的正是 car 这个变量,因而达到了 SetPrice 函数作用在 car 变量上的效果。

思考题:以上翻译还不完整,因为构造函数的作用没有体现出来。思考构造函数应该如何翻译。另外,静态成员函数和静态成员变量应如何翻译?

this 指针的作用

实际上,现在的C编译器从本质上来说也是按上面的方法来处理成员函数和对成员函数的调用的,即非静态成员函数实际上的形参个数比程序员写的多一个。多出来的参数就是所谓的“this指针”。这个“this指针”指向了成员函数作用的对象,在成员函数执行的过程中,正是通过“Ihis指针”才能找到对象所在的地址,因而也就能找到对象的所有非静态成员变量的地址。

下面程序的运行结果能够证明这一点:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
using namespace std;
class A
{
int i;
public:
void Hello(){ cout << "hello" << endl; }
};
int main()
{
A* p = NULL;
p -> Hello();
}

程序的输出结果是:
hello

在上面的程序中,p 明明是一个空指针,为何通过它还能正确调用 A 的成员函数 Hello 呢?因为,参考上面 C++ 到C程序的翻译,P->Hello()实质上应该是Hello(p),在翻译后的 Hello 函数中,cout 语句没有用到 this 指针,因此依然可以输出结果。如果 Hello 函数中有对成员变量的访问,则程序就会出错。

C++ 规定,在非静态成员函数内部可以直接使用 this 关键字,this 就代表指向该函数所作用的对象的指针。看下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using namespace std;
class Complex {
public:
double real, imag;
Complex(double r, double i) : real(r), imag(i) {}
Complex AddOne()
{
this->real++;
return *this;
}
};
int main()
{
Complex cl(1, 1), c2(0, 0);
c2 = cl.AddOne();
cout << c2.real << "," << c2.imag << endl; //输出 2,1
return 0;
}

第 9 行,this 指针的类型是 Complex*。因为 this 指针就指向函数所作用的对象,所以 this->rear 和 real 是完全等价的。*this代表函数所作用的对象,因此执行第 16 行,进入 AddOne 函数后,*this实际上就是 c1。因此的 c2 值会变得和 c1 相同。

奇葩用法

1
2
3
4
5
6
7
8
9
10
11
12
13
class This_cpp{
public:
This_cpp(){}
void print(){
printf("this = %s\n",this);
}
};

void this_test(){
const char* newp = "hello world";
This_cpp *p = (This_cpp*)newp;
p->print();
}