1. 程式人生 > >空指標的成員函式呼叫

空指標的成員函式呼叫

我一直認為技術是沒有止境的,不管你怎麼去學,總有你沒有掌握的地方。但是,人,是不能停下腳步的。

    今天在檢查一個MFC程式,看到GetSafeHwnd函式,於是讓我想明白到底它比m_hWnd成員變數safe在哪裡?到網上查了一下資料,發現了GetSafeHwnd的實現:

_AFXWIN_INLINE HWND CWnd::GetSafeHwnd() const {
           return this == NULL?NULL:m_hWnd;
}

    開始一看感覺不會吧?這有意義麼?this為NULL了,函式還能呼叫嗎?於是寫了一個簡單的程式來做測試,程式碼如下:

class A {
public:
     int i;
     void f()  {
         cout<<"Hello"<<endl;
     }
};


void main() {
     A * p= NULL;
     p->f();
}

    測試發現,程式能夠正常執行。把p賦一個非空值如p=(A*)123; 同樣如此。於是想搞明白這到底是怎麼一回事。雖然以前明白類成員函式中其實是隱藏了一個this指標,但不同的例項在呼叫函式時是如何工作的,還不是很清楚。通過這個測試,才能我徹底明白。

    對於一個類,在編譯好的彙編程式碼中,只有一份程式碼拷貝,但是有不同的例項空間。比如我們定義A a,b;我們呼叫a.f()和b.f()時,會跳轉到類程式碼中f()函式的實現部分。

    但如果你要引用成員變數,比如a.i,那麼程式需要找到a在記憶體中的地址,然後通過便宜量找到成員i的地址。如果f()中加上一句cout<<i<<endl的話,上面的程式碼很顯然會崩潰,為什麼呢?

    在函式的彙編程式碼中,有一個變數this,你在做函式類的函式呼叫的時候,類似與函式引數一樣壓到棧上去,雖然不同例項執行了同一段程式碼,因為this不同,得到的結果當然不同。如果你要存取成員變數,而this是一個非法地址,當然會引起崩潰。

    綜上可知,即時一個函式呼叫正常,還不能確定指標非空,很可能是因為成員函式中沒有使用成員變數的緣故。看來,這還真是一個搞笑的bug。

這是我自己的一段程式碼:

int _tmain(int argc, _TCHAR* argv[])
{
   
string leftString("test ");
   
string rightString(

"test");
    dstring
*praxis =new dstring();   

    praxis
->praxis1(leftString, rightString);
    delete praxis;
    praxis
= NULL;
    
    cout << praxis->praxis2() <<endl;
    

    system(
"pause");
   
return0;
}

delete釋放了praxis所指向的堆地址中的資料,並且指標被賦為空了, 當呼叫praxis->praxis2()時,雖然這個時候praxis指標已經空了,但由於Praxis2方法中沒有使用成員變數,所以,程式並沒有試圖去訪問堆,也就不會引起error或者將系統搞崩潰。

下面是全部的程式碼:

// dstring.h
//
#ifndef DSTRING_H
#define DSTRING_H#pragma once

#include
<string>usingnamespace std;
class dstring
{

public:
    dstring(
void);
   
void praxis1(const std::string&leftStr, const std::string&rightStr); string praxis2(void);
   
string praxis3(void);

public:
   
~dstring(void);
};
#endif// dstring.cpp
//

#include
"StdAfx.h"
#include
"dstring.h"
#include
<string>
#include
<stdio.h>
#include
<iostream>
#include
<cctype> dstring::dstring(void)
{
}

dstring::
~dstring(void)
{
}

void dstring::praxis1(conststring&leftStr, conststring&rightStr)
{
   
if (leftStr == rightStr)
    {
        cout
<<leftStr<<" and "<<rightStr<<"is equal"<<endl;
    }
   
else
    {
       
if (leftStr > rightStr)
        {
            cout
<<leftStr<<" is bigger than "<<rightStr<<endl;
        }
       
else
        {
            cout
<<leftStr<<" is smaller than "<<rightStr<<endl;
        }
    }
}


string dstring::praxis2(void)
{
   
string sentence, article;
   
bool firstEnter =true;

    cout
<<" Please enter sentences and end with Ctrl + Z."<< endl;
   
while (cin >> sentence)
    {   
       
if (!firstEnter)
        {
            sentence
=""+ sentence;
        }
        article
+= sentence;
        firstEnter
=false;
    }
   
return article;
}


string dstring::praxis3(void)
{
   
string sentence, article;
   
    cout
<<"Please enter a sentence with punctuatio."<<endl;
   
//cin >> sentence;while (cin >> sentence)
    {
        article
+= sentence;
    }

   
for (string::size_type index =0; index != article.size(); index ++)
    {
       
if (ispunct(article[index]))
        {
            article[index]
='';
        }
    }
   
return article;
}