2014-08-28 88 views
0

假設我有在C++中這個數據結構:C++字符串和指針操縱

struct Stash { 
    int size;  // Size of each space 
    int quantity; // Number of storage spaces 
    int next;  // Next empty space 
    // Dynamically allocated array of bytes: 
    unsigned char* storage; 
    // Functions! 
    void initialize(int size); 
    void cleanup(); 
    int add(const void* element); 
    void* fetch(int index); 
    int count(); 
    void inflate(int increase); 
};///:~ 

void Stash::initialize(int sz) { 
    size = sz; 
    quantity = 0; 
    storage = 0; 
    next = 0; 
} 

int Stash::add(const void* element) { 
    if(next >= quantity) // Enough space left? 
    inflate(increment); 
    // Copy element into storage, 
    // starting at next empty space: 
    int startBytes = next * size; 
    unsigned char* e = (unsigned char*)element; 
    for(int i = 0; i < size; i++){ 
    storage[(startBytes + i)] = e[i]; 

    } 

    next++; 
    return(next - 1); // Index number 
} 

void* Stash::fetch(int index) { 
    // Check index boundaries: 
    assert(0 <= index); 
    if(index >= next) 
    return 0; // To indicate the end 
    // Produce pointer to desired element: 
    int value = (index*size); 
    return &(storage[value]); 
} 

int Stash::count() { 
    return next; // Number of elements in CStash 
} 

void Stash::inflate(int increase) { 
    assert(increase > 0); 
    int newQuantity = quantity + increase; 
    int newBytes = newQuantity * size; 
    int oldBytes = quantity * size; 
    unsigned char* b = new unsigned char[newBytes]; 
    for(int i = 0; i < oldBytes; i++) 
    b[i] = storage[i]; // Copy old to new 
    delete []storage; // Old storage 
    storage = b; // Point to new memory 
    quantity = newQuantity; 
} 

void Stash::cleanup() { 
    if(storage != 0) { 
    cout << "freeing storage" << endl; 
    delete []storage; 
    } 
} ///:~ 

假設現在我使用的數據結構背誦串以這樣的方式

int main(){ 


    Stash* st1 = new Stash; 

    st1->initialize(sizeof(string)); 
    string s1 = "This is a GOOD morning"; 
    st1->add(&s1); 


    string s2 = "This is a BAD morning"; 
    st1->add(&s2); 


    string* s3; 
    s3 = static_cast<string*> (st1->fetch(0)); 
    cout << *s3 << endl; 

    string* s3; 
    s3 = static_cast<string*> (st1->fetch(1)); 
    cout << *s3 << endl; 

    st1->cleanup(); 

    delete st1; 

    return 0; 
} 

它工作!這是輸出:

This is a GOOD morning 
This is a BAD morning 

但在此相反:

int main(){ 


    Stash* st1 = new Stash; 

    st1->initialize(sizeof(string)); 
    string s1 = "This is a GOOD morning"; 
    st1->add(&s1); 

    s1 = "This is a BAD morning"; 
    st1->add(&s1); 

    string* s3; 
    s3 = static_cast<string*> (st1->fetch(0)); 
    cout << *s3 << endl; 

    string* s4; 
    s4 = static_cast<string*> (st1->fetch(1)); 
    cout << *s4 << endl; 

    st1->cleanup(); 

    delete st1; 

    return 0; 
} 

它不工作。這是輸出:

This is a BAD morning 
This is a BAD morning 

那麼,當我嘗試使用相同的參考時,機器中發生了什麼? 我已經嘗試過使用其他數據類型,它運行良好。

+0

'string'是'的std :: string'?因爲如果是的話,我看到這個班的一個非常邪惡的濫用... – fritzone 2014-08-28 10:27:06

+0

是的...我正在學習C++。非常感謝 – 2014-08-28 13:05:11

回答

0

您的代碼複製構成std::string容器的字節(而不是字符串數據中的字符)。這可能包含一個指向字符串數據的指針,再加上一個大小和容量。

當您編寫s1 = "stuff"時,std::string會在內部分配新內存,因此其先前的內部指針現在無效。

然後,您從數據結構中檢索以前的內部指針並嘗試使用它,導致未定義的行爲。

如果您的意圖是保存字符串中的字符,那麼您需要添加s1.c_str()而不是&s1

如果您的意圖是存儲任何對象的副本,那麼您需要調用複製構造函數來創建副本;不要像你目前正在做的那樣按位複製。如果您的目的是存儲對象並且不保留原始對象,則還可以調用移動構造函數移動賦值運算符

1

第一次使用S1的調用構造函數:

string s1 = "This is a GOOD morning"; 

然後您S1的地址添加到藏匿。接下來您有一個新的值賦給S1:

s1 = "This is a BAD morning"; 

這並不新建一個字符串 - 它調用的賦值操作符與一個新值替代相同string對象。然後保存S1的地址的另一個副本:

st1->add(&s1); 

如果你看看st1的數據,那麼你會看到你有相同的指針的兩個副本,都指向S1。這是預料之中的。在第一種情況下,您正在存儲指向包含不同值的兩個不同對象的指針。

+0

,但Stash數據結構似乎記住了字符串的字節而不是地址。事實上,該程序與其他數據類型很好......所以我不知道...... :-( – 2014-08-28 10:34:19

+0

啊,只是注意到了。問題是你正在處理指向字符串對象的指針,就好像它是一個' char'數組,所以你存儲的是指向'string'的指針,而不是字符數組。 – 2014-08-28 10:43:50

0

在第二種方法中,您已經使用s1的地址存儲在堆棧中。而且你並沒有複製堆棧中的內容,所以當你改變s1來讓不同的內容先前的內容也發生改變時,因爲你實際上是將指針推向了內容的副本。 例如,如果你做到以下幾點(內容複製到用於推動一個新的字符串),這個工程:

string s1 = "This is a GOOD morning"; 
st1->add(new string(s1)); 

s1 = "This is a BAD morning"; 
st1->add(new string(s1));