2

C++指针与引用(Pointers OR References) - 阮春义

 2 weeks ago
source link: https://www.cnblogs.com/ruanchunyi/p/18169059
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

C++指针与引用(Pointers OR References)

一、Pointers

Pointer是指针,可以用来指向任何一个objects,包括一般变量:

1 int  i = 3;
2 int * pi = &i;
3 cout << pi << endl; // 0x0064FDF0
4 cout << *pi << endl; // 3

  此时pi本身内含i的地址,要取出pi所指向的object,可以使用*运算符(dereference operator).Pointer也可以用来指向任何一个class type objects。由于下面的pPoint指向一整个大结构(一个object),如果要取用其中的members(data members 或member functions都可以,只要他们的封装等级是public),必须使用—>运算符(arrow operator),例如:

1 class CPoint { public: float _x, _y, _z; };
2 CPoint  * pPoint = new CPoint;
3 cout << pPoint << endl; // 0x00770560
4 pPoint->_x = 9.28;
5 cout << pPoint->_x << endl; // 9.28

  Pointer 甚至可以指向一个不明对象(void):

1 void * pv;
2 pv = malloc(1024); // 配置 1024 bytes(来自 heap)
3 cout << pv << endl; // 0x00760A14

  此时如果要提取 pv 所指的对象,不可以,会出现编译出错:

1 cout << *pv << endl; // error C2100: illegal indirection

  也说是说,pointer本身没有意义,它的意义来自于它的类型,因此,将pointer前进一个单位,究竟是前进多少个bytes呢?那要看pointer的类型,如果将int *pi和CPoint *pPoint各加1,得到:

1 cout << ++pi << endl; // 0x0064FDF4,比原先增加 4
2 cout << ++pPoint << endl; // 0x0077056C,比原先增加 12

  这是因为在32位的系统中int为4 bytes,而我们设计的CPoint里面有一个float数据,大小为12bytes,所以各指针累加1时,分别前进4bytes和12bytes。如果把一个指向不明对象的指针加1,会得到什么结果呢?不会有结果,只会编译出错:

1 cout << ++pv << endl; // error C2036: 'void *' : unknown size

  当然,如果你做了强转型(cast)动作,就可以解决“不明对象”的问题,因为你赋予了该指针一个明确的类型,例如:

1 double * pd = (double*)pv; // 强转型。double is 8 bytes.
2 cout << pd << endl; // 0x00760A14
3 cout << ++pd << endl; // 0x00760A1C,比原先增加 8

  下表是上述验证结果的一个整理:

1447648-20240429221835811-914602953.png

   当我们开始设计classes继承体系,有许多时候需要把一个pointer指向一个类型不符的object(但彼此类型又有继承的关系存在,这其实正是polymorphism的一个精髓),这时候类型的转换就非常重要。强制转型太过粗暴,在不够安全的时候仍然强转换,存在风险。C++有其它更精致的转型工具,我们后续再谈。

  Pointer不但可以指向object,还可以指向class的data members或member functions。它们的形式有点怪,结果也可能出人意外,这些问题我们后续再谈。

二、References

  与pointer常常相提并论,并且常常被混淆不清的是所谓的reference。Reference(&)像是一个常数指针,可以被自动提取(dereference)。下面这个例子就是使r成为x的一个reference:

1 int x;
2 int& r = x; // r is a reference of x

  当一个refernce产生,它必须被初始化为某个原已存在的object,像上面那样,如查我写:

1 int& q = 12;

  那么编译器会先配置一块int内存空间,将内容设为12,然后把q这个reference“捆绑”到该空间上。重点是,任何reference都必须被“捆绑”到某一个空间,成为一个"化身“。当你处理该reference,你就是在处理那个被捆绑的空间。如果:

1 int x = 0;
2 int& r = x; // r is a reference of x
3 int* p = &x; // p is a pointer to x
4 r++; // 请注意:sizeof(r) == sizeof(x)

  那么r和x的现值都为1,因为增加r的值就是增加x 的值。

1447648-20240430123531377-729755608.png

   面对reference,最简单的想像就是,把它幻想为一个形式漂亮的pointer。这个形式漂亮的pointer好处是,我们不需要担心它是否被初始化(编译器会强迫做),也需要担心何提取(dereference)它(同样的,编译器会负责)。Refernce虽然在本质上是一个指针,在形式却是个object。也就是因为其形式漂亮,而本质实用(用于call by refernce),所以refernce常被用于函数的参数列表(arguments list)和回传值(return value)。下面就是个例子:

 1 int* funcl(int* x)
 2 {
 3     (*x)++;
 4     return x;
 5 }
 6 
 7 int& func2(int& x)
 8 {
 9     x++;
10     return x;
11 }
12 
13 int main()
14 {
15    int a=0;
16    //ugly but explictit,你可以你可以清楚看到传給 func1() 的是个指针。
17    //传回值的形式也很「难看」。
18    cout<<*funcl(&a)<<endl;
19    // clean but hidden。传給 func2() 的其实是个指针(借助 reference)。
20    // 但你看不出來。传回的也是指针(借助 reference),你也看不出來。
21    cout<<func2(a)<<endl;
22    23     return 0;
24 }

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK