2009-11-21 71 views
0

我已經開發了一個基於數組的哈希表實現,包含幾個股票名稱,符號,價格等。我需要從我的陣列中刪除一個股票。我被告知使用delete操作符是不好的面向對象的設計。面向刪除的好的面向對象設計是什麼?C++中的安全刪除

bool hash::remove(char const * const symbol, stock &s, 
int& symbolHash, int& hashIndex, int& usedIndex) 
{ 
symbolHash = this->hashStr(symbol); // hash to try to reduce our search. 
     hashIndex = symbolHash % maxSize; 
     usedIndex = hashIndex; 

if ( hashTable[hashIndex].symbol != NULL && 
    strcmp(hashTable[hashIndex].symbol , symbol) == 0) 
{ 
    delete hashTable[hashIndex].symbol; 
    hashTable[hashIndex].symbol = NULL; 
    return true; 
} 
for (int myInt = 0; myInt < maxSize; myInt++) 
{ 
    ++usedIndex %= maxSize; 
    if (hashTable[usedIndex].symbol != NULL && 
    strcmp(hashTable[usedIndex].symbol , symbol) == 0) 
    { 
    delete hashTable[usedIndex].symbol; 
    hashTable[usedIndex].symbol = NULL; 
    return true; 
    } 
} 
return false; 
} 

注意到,我有一隻股票& S作爲參數,我可以這樣使用它:

s = &hashTable[usedIndex]; 
delete s.symbol; 
s.symbol = NULL; 
hashTable[usedIndex] = &s; 

這樣確實可以但是,它會導致內存泄漏。即使如此,我不確定它是否是好的對象orinted設計。

這是我的標題,股票和所有的東西被初始化和定義。

//hash.h

private: 
friend class stock; 
int isAdded; // Will contain the added hash index. 
// Test for empty tables. 
// Can possibly make searches efficient. 
stock *hashTable; // the hashtable will hold all the stocks in an array 

}; 

//哈希表構造函數

hash::hash(int capacity) : isAdded(0), 
hashTable(new stock[capacity]) // allocate array with a fixed size 
{ 
if (capacity < 1) exit(-1); 

    maxSize = capacity; 

// We can initialize our attributes for the stock 
// to NULL, and test for that when searching. 
for (int index = 0; index < maxSize; index++) 
{ 
    hashTable[index].name = NULL; 
    hashTable[index].sharePrice = NULL; 
    hashTable[index].symbol = NULL; 
} 
} 

// stock.h

... 友元類的HashMap;

private: 

const static int maxSize; // holds the capacity of the hash table minus one 
date priceDate; // Object for the date class. Holds its attributes. 
char *symbol; 
char *name; 
int sharePrice; 
}; 

我的問題仍然只是,我如何執行安全刪除?

s = &hashTable[usedIndex]; 
delete s.symbol; 
s.symbol = NULL; 
hashTable[usedIndex] = &s; 

這似乎工作,但導致內存泄漏!這是如何安全地完成的?

刪除hashTable [usedIndex] .symbol; hashTable [usedIndex] .symbol = NULL; < - 沒有這樣做。

數組中插槽的狀態(空等)不應記錄在庫存實例中。這是不好的面向對象的設計。相反,我需要將陣列插槽的狀態存儲在陣列插槽本身中。

我該怎麼做?

+2

我不確定刪除操作符,但是有大量的const char *而不是字符串,並且重新設計輪子H^H^H而不是地圖是重要的設計。 – hirschhornsalz 2009-11-21 01:06:53

+4

使用刪除與面向對象的設計無關,誰告訴你不知道他們在說什麼。如果你在堆上分配內存來刪除它,那麼這段時間。現在我們嘗試使用像RAII這樣的習語來封裝堆內存管理的過程,但是在某些時候,內存仍然需要被釋放。 – 2009-11-21 01:07:27

+2

這裏沒有足夠的信息來回答這個問題。你設法傳達的唯一事實是:你有一個可以被錯誤使用的類,因爲你可以公開訪問你的結構的內部部分。這就是糟糕的OO設計。該類的用戶不需要知道或能夠操縱內部結構或修改將使其處於不一致狀態的實例。 – 2009-11-21 01:17:55

回答

2

注意:這個答案只是解決 一些事情你正在做 不正確。這不是最好的方式 做你在做什麼。一組 stock**會更有意義。

您的股票類沒有構造函數嗎?你並不需要了解股票類什麼,如果它是一個正確的對象:

hash::hash(int capacity) // change this to unsigned and then you can't have capacity < 0 
    : isAdded(0) 
    , hashTable(0) // don't call new here with bad parameters 
{ 
    if (capacity < 1) exit(-1); // this should throw something, maybe bad_alloc 

    maxSize = capacity; 
    hashTable = new stock[capacity]; // this calls the stock() constructor 

    // constructor already called. All this code is useless 
    // We can initialize our attributes for the stock 
    // to NULL, and test for that when searching. 
// for (int index = 0; index < maxSize; index++) 
// { 
//  hashTable[index].name = NULL; 
//  hashTable[index].sharePrice = NULL; 
//  hashTable[index].symbol = NULL; 
// } 
} 

class stock { 
    char* name; // these should be std::string as it will save you many headaches 
    char* sharePrice; // but I'll do it your way here so you can see how to 
    char* symbol; // avoid memory leaks 
public: 
    stock() : name(0), sharePrice(0), symbol(0) {} 
    ~stock() { delete[] name; delete[] sharePrice; delete[] symbol; } 
    setName(const char* n) { name = new char[strlen(n)+1]; strcpy(name, n); } 
    setPrice(const char* p) { sharePrice = new char[strlen(p)+1]; strcpy(sharePrice, p); } 
    setSymbol(const char* s) { symbol = new char[strlen(s)+1]; strcpy(symbol, n); } 

    const char* getName() const { return name; } 
    const char* getPrice() const { return sharePrice; } 
    const char* getSymbol() const { return symbol; } 
} 
1

它看起來像你正在使用(或試圖使用)開放尋址與衝突解決線性探測。在這種情況下,您需要以某種方式將項目標記爲已刪除(而不是空白),以便您仍可以訪問已刪除項目之後的項目。否則,您將無法查找某些項目,因爲如果發現已刪除的存儲桶,您的探測序列將被提前終止。因此,您將無法再訪問表格中的某些項目,這可能是您獲得內存泄漏的原因。

基本上,你應該從散列索引開始,比較項目和你的密鑰,然後如果它不等於你的密鑰,增加到下一個索引並重復,直到找到該項目,或者直到遇到空桶。如果找到該項目,請刪除該項目並將該索引標記爲已刪除。但重要的是,您有一些方法可以區分空的哈希桶和已刪除的哈希桶,否則刪除的桶會導致您儘早終止探測序列。至於「良好的面向對象的設計」,沒有面向對象編程的固有屬性,它必然使得delete成爲一個糟糕的設計。每個分配內存的數據結構必須以某種方式釋放它。你可能指的是這樣的事實:實施不管理自己的記憶的班級通常更安全和更少的工作,而是將該責任委託給預製集裝箱班級,例如std::vectorstd::string

1

要獲得良好的面向對象的設計,集合應該不知道存儲在其中的內容。這實際上與使用delete運算符本身無關,但需要一個對象(在本例中爲stock)來存儲特定於數據結構的代碼。

我可以看到有兩種計劃可以快速解決此問題。

  1. 使用的stock *,而不是僅僅stock陣列。然後,空值將意味着該插槽已打開,並且非空值將意味着該插槽可以使用。在這個計劃中,當插入對象時,你會在整個對象上調用新的和刪除的對象,然後刪除對象,而不僅僅是符號。

  2. 創建一個包裝stock項目的HashSlot類,添加所需的簿記值。 您的散列表將會是一個HashSlot項目的數組。

我更喜歡第二個。無論哪種情況,stock都應該有一個析構函數來清除自己的內存。

+0

我輕度不同意你的第一句話。不可知論集合可能是首選,但Boost.Intrusive顯示有時對象知道它所在的集合。 – jmucchiello 2009-11-21 04:57:23

1

我已經開發了一個Hashtable的基於陣列的實現與一些股票名稱,符號,價格等。我需要從我的陣列中刪除一個股票。我被告知使用delete操作符是不好的面向對象的設計。面向刪除的好的面向對象設計是什麼?

那麼,面向對象設計的關鍵原理之一是可重用性

因此,唯一良好的面向對象的設計是重用已爲您開發的解決方案!

C++自帶一個完美的好的map類。最近的編譯器也支持TR1,它在名稱unordered_map下添加了一個哈希表。

Boost庫還包含一個unordered_map的實現,以防萬一遇到沒有TR1支持的編譯器。

至於你的問題有關delete
我不知道誰告訴你的delete是「壞的面向對象設計」,或者爲什麼,但他們威力意味着是,它是不好的C++設計

一個共同的準則是,你永遠不應該明確地呼籲delete。相反,它應該通過使用成語隱含地調用。

每當您創建一個必須在稍後的某個時間點被刪除的資源時,您會將其包裝在一個小堆棧分配對象中,該對象的析構函數會爲您調用delete

這保證當RAII對象超出範圍時它被刪除,不管如何您離開作用域。即使拋出異常,對象仍然被清除,析構函數被調用,並且資源被刪除。如果您需要更復雜的方式來管理對象的生命週期,則可能需要使用智能指針,或者僅使用複製構造函數和賦值運算符來擴展RAII包裝,以允許複製或移動資源的所有權。

這是很好的C++實踐,但有沒有什麼與面向對象的設計。並非所有事都做OOP不是編程的聖盃,也不是的一切必須是OOP。好的設計比好的面向對象更重要。