• C++ 中“空引用”与“空指针”的区别


    网络上有很多讨论C++ 的“引用”与“指针“的区别的文章,谈到区别,其中有一条:“引用不能为空(NULL),引用必须与合法的存储单元关联,指针则可以是NULL)”,但在实际应用中,有时候为了保持一致性,我们会抛开这个规则,人为创造出一个“空引用”。

    很多情况下,“空引用”确实可以工作,以致于“引用不能为空”的忠告,被嘲笑为形式主义,仅仅是标准制定者的耸人听闻。一个“空引用”的例子是:

    int * a = NULL;
    int & b = *a;

    于是当访问b的时候,程序异常出现了:

    void f(int & p)
    {
        p = 0;
    }
    f(b);

    当然,可以增加点判断,修正这个问题:

    void f(int & p)
    {
        if (&p) p = 0;
    }

    怎么样,是不是有点别扭?但是如果换成成指针,你要输入的字符数是一模一样的:

    void f(int * p)
    {
        if (p) *p = 0;
    } 

     于是,到底是使用“引用”还是“指针”,好像就是智者见智仁者见仁的事情了。

    然而,然而。。。。。。

    这真的一样吗?

    我们来看看复杂一点的例子:

    // test.cpp
    
    #include <iostream>
    
    class A
    {
        int a;
    };
    
    class B
    {
        int b;
    };
    
    class C
    : public A, public B
    {
        int c;
    };
    
    void fb(B & b)
    {
        std::cout << &b << std::endl;
    }
    
    void fb(B * b)
    {
        std::cout << b << std::endl;
    }
    
    int main(int argc, char* argv[])
    {
        C * c = NULL;
    
        fb(c);
    
        fb(*c);
    
        return 0;
    }

    编译运行一下看看:

    $ ./test
    0
    0x4

    咦,怎么&b不是0,也就是不是“空引用”了,这时候,即使加上判断,if (&b),也无济于事了。

    大家也许注意到了,上面是linux环境运行,那么windows环境呢:

    >test.exe
    00000000
    00000000

    这时候,“空引用”保持了他的“空”属性,仅在windows平台做C++的开发者,可以松口气了。

    这是怎么回事呢,是你的眼睛欺骗了你?也许是,但是CPU不会欺骗我们,从汇编代码可以看出本质。下面是linux平台编译的代码:

    Dump of assembler code for function main:
    0x0804870a <+0>: push %ebp
    0x0804870b <+1>: mov %esp,%ebp
    0x0804870d <+3>: and $0xfffffff0,%esp
    0x08048710 <+6>: sub $0x20,%esp
    0x08048713 <+9>: movl $0x0,0x1c(%esp)
    0x0804871b <+17>: cmpl $0x0,0x1c(%esp)
    0x08048720 <+22>: je 0x804872b <main+33>
    0x08048722 <+24>: mov 0x1c(%esp),%eax
    0x08048726 <+28>: add $0x4,%eax
    0x08048729 <+31>: jmp 0x8048730 <main+38>
    0x0804872b <+33>: mov $0x0,%eax
    0x08048730 <+38>: mov %eax,(%esp)
    0x08048733 <+41>: call 0x80486df <fb(B*)>
    0x08048738 <+46>: mov 0x1c(%esp),%eax
    0x0804873c <+50>: add $0x4,%eax
    0x0804873f <+53>: mov %eax,(%esp)
    0x08048742 <+56>: call 0x80486b4 <fb(B&)>
    0x08048747 <+61>: mov $0x0,%eax
    0x0804874c <+66>: leave 
    0x0804874d <+67>: ret 

    这是windows平台的:

    wmain:
    004114D0 push ebp 
    004114D1 mov ebp,esp 
    004114D3 sub esp,0DCh 
    004114D9 push ebx 
    004114DA push esi 
    004114DB push edi 
    004114DC lea edi,[ebp-0DCh] 
    004114E2 mov ecx,37h 
    004114E7 mov eax,0CCCCCCCCh 
    004114EC rep stos dword ptr es:[edi] 
    004114EE mov dword ptr [c],0 
    004114F5 mov eax,dword ptr [c] 
    004114F8 mov dword ptr [rc],eax 
    004114FB cmp dword ptr [c],0 
    004114FF je wmain+3Fh (41150Fh) 
    00411501 mov eax,dword ptr [c] 
    00411504 add eax,4 
    00411507 mov dword ptr [ebp-0DCh],eax 
    0041150D jmp wmain+49h (411519h) 
    0041150F mov dword ptr [ebp-0DCh],0 
    00411519 mov ecx,dword ptr [ebp-0DCh] 
    0041151F push ecx 
    00411520 call fb (411118h) 
    00411525 add esp,4 
    00411528 cmp dword ptr [rc],0 
    0041152C je wmain+6Ch (41153Ch) 
    0041152E mov eax,dword ptr [rc] 
    00411531 add eax,4 
    00411534 mov dword ptr [ebp-0DCh],eax 
    0041153A jmp wmain+76h (411546h) 
    0041153C mov dword ptr [ebp-0DCh],0 
    00411546 mov ecx,dword ptr [ebp-0DCh] 
    0041154C push ecx 
    0041154D call fb (41108Ch) 
    00411552 add esp,4 
    00411555 xor eax,eax 
    00411557 pop edi 
    00411558 pop esi 
    00411559 pop ebx 
    0041155A add esp,0DCh 
    00411560 cmp ebp,esp 
    00411562 call @ILT+345(__RTC_CheckEsp) (41115Eh) 
    00411567 mov esp,ebp 
    00411569 pop ebp 
    0041156A ret 

    汇编代码有兴趣自己研究,不细说了。


    回过头想想,两个平台的编译器的两种处理方式,都有他的合理性,windows平台增加了容错性,而linux平台在处理引用时减少判断,增加性能。这隐隐体现出windows与linux开发理念的不同。

    最后,请大家记住,引用不能为空,如果可能存在空对象时,请使用指针。


    原文链接:https://blog.csdn.net/luansxx/article/details/10134139

  • 相关阅读:
    js 数组详解(javascript array)
    CentOS 修改IP地址, DNS, 网关
    Leetcode 652.寻找重复的子树
    Leetcode 650.只有两个键的键盘
    Leetcode 649.Dota2参议院
    Leetcode 648.单词替换
    Leetcode 647.回文子串
    Leetcode 645.最长数对链
    Leetcode 643.子数组最大平均数I
    Leetcode 640.求解方程
  • 原文地址:https://www.cnblogs.com/2018shawn/p/12724525.html
Copyright © 2020-2023  润新知