2012-12-11 42 views
1

我應該得到相同的兩行.. 發生什麼事,我得到兩個不同的值..就像是針對不同的崗位.. 我認爲錯誤是裏面的d->add(*b)C++向量失去指針引用

輸出

thiago 14333804 
    Ph¿├┌ 2816532 

來形容的話更好,我把下面

我有一個程序代碼

int main(int argc, char **argv) { 

    CClass* c = new CClass(); 

    BClass* b = c->getNext(); 

    printf("%s %d \n", b->getValue(), b->getValue()); 

    DClass* d = new DClass(); 
    d->add(*b); 

    printf("%s %d \n", d->getNext(), d->getNext()); 

    cin.get(); 

    return 0; 
} 

的接口都在下面

class BClass 
{ 
private: 
    char* value; 
    bool stale; 
public: 
    BClass(char* value); 
    ~BClass(void); 
    char* getValue(); 
    bool isStale(); 
}; 


class CClass 
{ 
private: 
    vector<BClass*> list; 
public: 
    CClass(void); 
    ~CClass(void); 

    BClass* getNext(); 
}; 


class DClass 
{ 
private: 
    vector<BClass*> list; 
    static bool isStale(BClass* b) { return b->isStale();}; 
public: 
    DClass(void); 
    ~DClass(void); 
    void add(BClass s); 
    char* getNext(); 
}; 

和實現如下

//BClass 

BClass::BClass(char* value) 
{ 
    this->value = value; 
    this->stale = false; 
} 

BClass::~BClass(void) 
{ 
} 

char* BClass::getValue() 
{ 
    return value; 
} 

bool BClass::isStale() 
{ 
    return stale; 
} 



//CClass 

CClass::CClass(void) 
{ 
    list.push_back(new BClass("thiago")); 
    list.push_back(new BClass("bruno")); 
    list.push_back(new BClass("carlos")); 
} 


CClass::~CClass(void) 
{ 
} 

BClass* CClass::getNext() 
{ 
    return list.at(0); 
} 

//DClass 

DClass::DClass(void) 
{ 
} 

DClass::~DClass(void) 
{ 
} 

void DClass::add(BClass s) 
{ 
    list.push_back(&s); 
} 

char* DClass::getNext() 
{ 
    BClass* b = list.at(0); 

    return b->getValue(); 
} 
+1

指針地址會有所不同,但什麼指針指向可以相同 – dchhetri

+0

的地址,你傳遞一個對象,以添加副本而不是指針。這就是你得到一個位於棧上的臨時對象的地址,它的作用域在add()函數內部。 – gregory561

回答

5

當您在B類的一個實例傳遞到D::add()功能您創建對象的深層副本和複製是放在堆棧上的東西。稍後,您使用該副本的地址將其推入列表中。一旦功能完成,這個自動變量超出了範圍,因此你用來放入列表的指針不再有效。

要解決改變你的接口,以避免深拷貝如下:

void DClass::add(BClass * s) 
{ 
    list.push_back(s); 
} 

步驟一步的你的代碼是做

  1. BClass* b = c->getNext(); //你的地址列表中的第一個元素(在構造函數中創建)並將其分配給b
  2. d->add(*b); // the * b將引用指向b的對象ÿb並把它入堆棧以準備調用add()
  3. void DClass::add(BClass s){ //一個解除引用的對象的深拷貝被放入該函數的堆棧幀
  4. list.push_back(&s); //的的那臨時拷貝的地址原始對象正被用於添加到您的列表中
  5. } //這就是樂趣發生的地方 - 一旦函數完成,它將展開堆棧備份,之前由該臨時副本佔用的內存將被重新 - 用於其他目的。在你的情況下 - 它將用於傳遞參數到函數d->getNext()(總是有一個隱藏的這個參數給非靜態成員函數),後來到printf()函數。記住 - 你以前的指針指向臨時副本仍然指向到堆棧,但它現在由不同的數據佔用,導致你看到腐敗

一般的經驗法則 - 從未使用指針來臨時對象;-)

+0

@YePhlicK是不是有我保證,即使在這個功能範圍之後s的地址總是有效的? – thiagoh

+0

只有地址在堆或者如果你正在做一些真的* *時髦的黑客攻擊,並確保堆棧幀沒有被重複使用(這是在他們的腦子應該想過這樣做的一個非常重要的任務,沒有人它,除了非常特殊的應用程序,如反調試mesures) – YePhIcK

1

DClass::add函數中,BClass s是一個局部變量。

void DClass::add(BClass s) 
{ 
    list.push_back(&s); 
} 

當你調用d->add(*b);,你是按值傳遞一個BClass,這意味着你就創建了一個副本,該副本的地址是不是原來的地址相同。

s會盡快走出去的範圍爲函數返回,並指向它的指針將是無效的。所以存儲這個指針對你來說並不好,因爲解引用它將是未定義的行爲。

+0

是不是有無論如何保證's'的地址即使在這個函數範圍之後總是有效的? – thiagoh

+0

據我所知,沒有什麼可以做的,以避免's'本身超出範圍。你可以編寫一個構造函數來複制這個對象並創建一個'new BClass(s​​)',並存儲這個指針,但是我認爲你想要用另一個解決方案,只需將'add(BClass s)'改爲'添加(BClass * s)'並傳遞指針而不是複製。 – filipe