簡體   English   中英

C ++對象:我應該何時使用指針或引用

[英]C++ Objects: When should I use pointer or reference

我可以使用一個對象作為它的指針或它的引用。 我知道不同之處在於必須手動刪除指針,並且引用仍然存在,直到它們超出范圍。

我什么時候應該使用它們? 有什么實際區別?

這些問題都沒有回答我的疑慮:

引用基本上是一個帶有限制的指針(必須在創建時綁定,不能反彈/ null)。 如果您的代碼使用這些限制是有意義的,那么使用引用而不是指針允許編譯器警告您意外違反它們。

它與const限定符非常相似:語言可以在沒有它的情況下存在,它只是作為一種額外的獎勵功能,可以更容易地開發安全的代碼。

“指針我必須刪除並引用它們,直到它們的范圍完成。”

不,那是完全錯誤的。

必須刪除用new分配的對象[*]。 不得刪除未使用new分配的對象。 可以有一個指向未分配new的對象的指針,並且可以引用一個用new分配的對象。

指針或引用是訪問對象的一種方式,但不是對象本身,並且與對象的創建方式無關。 概念上的區別在於引用是對象的名稱,而指針是包含另一個對象的地址的對象。 實際的差異,你如何選擇使用哪一個,包括每個的語法,以及引用不能為空且無法重新安裝的事實。

[*] delete 必須使用delete[]刪除使用new[]分配的數組。 有一些工具可以幫助跟蹤分配的資源並為你做這些調用,稱為智能指針,因此,自己明確地進行調用應該是非常罕見的,而不是僅僅安排它來完成,但不過它必須完成。

您有許多情況,其中參數不存在或無效,這可能取決於代碼的運行時語義。 在這種情況下,您可以使用指針並將其設置為NULL(0)以指示此狀態。 除此之外,

  • 可以將指針重新分配給新狀態。 參考不能。 在某些情況下這是可取的。
  • 指針有助於轉移所有者的語義。 這在多線程環境中尤其有用,如果參數狀態用於在單獨的線程中執行,並且通常不會輪詢直到線程退出。 現在線程可以刪除它。

suszterpatt已經給出了很好的解釋。 如果你想要一個容易記住的經驗法則,我建議如下:

如果可能的話,使用引用,只有在無法避免的情況下才使用指針。

更短:更喜歡引用指針。

這是另一個答案(也許我應該編輯第一個,但由於它有不同的焦點,我認為將它們分開是可以的)。

當你用new創建一個指針時,它的內存是保留的,它會一直存在,直到你調用delete為止 - 但標識符的生命周期仍然限於代碼塊的結尾。 如果在函數中創建對象並將它們附加到外部列表,則在函數返回后,對象可以安全地保留在內存中,您仍然可以在沒有標識符的情況下引用它們。

這是Umbra的一個(簡化)示例,Umbra是我正在開發的C ++框架。 存儲在引擎中的模塊列表(指向對象的指針)。 引擎可以將對象附加到該列表:

void UmbraEngine::addModule (UmbraModule * module) {
    modules.push(module);
    module->id = modules.size() - 1;
}

檢索一個:

UmbraModule * UmbraEngine::getModule (int id) {
    for (UmbraModule **it=modules.begin(); it != modules.end(); it++) {
        if ((*it)->id == id) return *it;
    }
}

現在,我可以在不知道其標識符的情況下添加和獲取模塊:

int main() {
    UmbraEngine e;
    for (int i = 0; i < 10; i++) {
        e.addModule(new UmbraModule());
    }
    UmbraModule * m = e.getModule(5); //OK
    cout << m << endl; //"0x127f10" or whatever
    for (int j = 0; k < 10; j++) {
        UmbraModule mm; //not a pointer
        e.addModule(&mm);
    }
    m = e.getModule(15);
    cout << m << endl; //{null}
}

模塊列表在整個程序期間都會持續存在,如果模塊的實例化為new ,我不需要關心模塊的生命周期。 所以基本上它 - 使用指針,你可以擁有永遠不需要標識符(或名稱,如果你願意)的長壽命對象,以便引用它們:)。

另一個不錯但非常簡單的例子是:

void getVal (int * a) {
    *a = 10;
}
int main() {
    int b;
    getVal(&b);
    return b;
}

呃...不完全是。 它是具有范圍的IDENTIFIER。 當你使用new創建一個對象,但是它的標識符的范圍結束時,你可能最終會出現內存泄漏(或者不是 - 取決於你想要實現的內容) - 對象在內存中,但你無法引用它了。

區別在於指針是內存中的地址,所以如果你有這樣的代碼:

int * a = new int;

a是指針。 你可以打印它 - 你會得到類似“0x0023F1”的東西 - 它只是:一個地址。 它沒有值(盡管某些值存儲在該地址的內存中)。

int b = 10;

b是一個值為10的變量。如果你打印它,你將獲得10

現在,如果你想要a指向b的地址,你可以這樣做:

a = &b; //a points to b's address

或者如果您希望a指向的地址具有b的值:

*a = b; //value of b is assigned to the address pointed by a

請編譯此示例並注釋/取消注釋第13行和第14行以查看差異(請注意標識符指向何處以及WHAT VALUE)。 我希望輸出結果不言自明。

#include <iostream>

using namespace std;

int main()
{
    int * a = new int;
    int b = 10;
    cout << "address of a: " << a << endl;
    cout << "address of b: " << &b << endl;
    cout << "value of a: " << *a << endl;
    cout << "value of b: " << b << endl;
    a = &b; //comment/uncomment
    //*a = b; //comment/uncomment
    cout << "address of a: " << a << endl;
    cout << "address of b: " << &b << endl;
    cout << "value of a: " << *a << endl;
    cout << "value of b: " << b << endl;
}

我們先回答最后一個問題。 那么第一個問題會更有意義。

問:“指針和參考之間的實際區別是什么?”

答:引用只是另一個變量的本地假名。 如果通過引用傳遞參數,則該參數與調用語句中列出的參數完全相同。 但是,內部通常指針和引用之間沒有區別。 引用提供了“語法糖”,允許您減少當您真正想要訪問給定變量的單個實例時必須執行的鍵入操作。

問:“我應該何時使用每個人?”

答:這將是個人偏好的問題。 這是我遵循的基本規則。 如果我將需要操作另一個范圍中的變量,並且該變量是一個內部類型,一個應該像內在類型一樣使用的類(即std :: string等等),或者一個const類實例,然后我通過引用傳遞。 否則,我將通過指針。

問題是你不能重新引用另一個對象的引用。 引用綁定編譯時間,不能為null或反彈。 所以如果你的疑問是那么指針並不是多余的:)

正如我的c ++老師曾經說過的那樣,指針指向內存位置,而引用是別名 因此,主要優點是它們可以與它們引用的對象名稱相同的方式使用,但是在通過將對象傳遞到該對象的范圍內。

雖然指針可以重定向到其他位置,但是引用就像常量指針一樣,無法重定向。 所以引用不能用於在函數等中遍歷數組。

但是,指針是一個單獨的實體會占用一些內存,但引用與引用的對象相同不會占用任何額外的空間。 這是它的優點之一。

我還讀到參考的處理時間較少,

int&i = b;

i ++; 花費的時間比

int * j = b;

(* j)++;

但我還沒有證實這一點。 如果有人能夠對這一主張有所了解,那就太好了。

歡迎評論:)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM