C++ C+中的安全删除+;

C++ C+中的安全删除+;,c++,C++,我开发了一个基于数组的哈希表实现,其中包含几个股票名称、符号、价格等。我需要从阵列中移除一个库存。我被告知使用delete操作符是糟糕的面向对象设计。什么是好的面向对象的删除设计 bool hash::remove(char const * const symbol, stock &s, int& symbolHash, int& hashIndex, int& usedIndex) { symbolHash = this->hashStr( symbol

我开发了一个基于数组的哈希表实现,其中包含几个股票名称、符号、价格等。我需要从阵列中移除一个库存。我被告知使用delete操作符是糟糕的面向对象设计。什么是好的面向对象的删除设计

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;
}
这确实有效,但是会导致内存泄漏。即使如此,我也不确定这是不是一个好的面向对象的设计

这是我的标题,股票和所有这些东西都是在这里初始化和定义的

//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;
 }
}
我的问题仍然是,如何进行安全移除

s = &hashTable[usedIndex];
delete s.symbol;
s.symbol = NULL;
hashTable[usedIndex] = &s;
这似乎可行,但会导致内存泄漏!这是如何安全完成的

删除哈希表[usedIndex]。符号;
哈希表[usedIndex]。符号=NULL 看起来您正在使用(或试图使用)带线性探测的开放寻址来解决冲突。在这种情况下,您需要以某种方式将项目标记为已删除(而不是空),以便您仍然可以访问位于已删除项目之后的项目。否则,您将无法查找某些项目,因为如果您的探测序列发现已删除的bucket,它将提前终止。因此,您将无法再访问表中的某些项,这可能就是内存泄漏的原因

基本上,您应该从散列索引开始,将该项与您的键进行比较,然后如果它不等于您的键,则递增到下一个索引并重复,直到找到该项,或者直到遇到一个空桶为止。如果找到该项目,请删除该项目并将该索引标记为已删除。但重要的是,您有一些方法来区分空哈希桶和已删除的哈希桶,否则已删除的哈希桶将导致您提前终止探测序列


至于“良好的面向对象设计”,面向对象编程的固有特性并不一定会使使用
delete
成为糟糕的设计。每个分配内存的数据结构都必须以某种方式释放内存。您可能指的是这样一个事实:实现不管理自己内存的类通常更安全,工作量更少,而是将该职责委托给预先制作的容器类,如
std::vector
std::string

看起来您正在使用(或试图使用)使用线性探测打开寻址以解决冲突。在这种情况下,您需要以某种方式将项目标记为已删除(而不是空),以便您仍然可以访问位于已删除项目之后的项目。否则,您将无法查找某些项目,因为如果您的探测序列发现已删除的bucket,它将提前终止。因此,您将无法再访问表中的某些项,这可能就是内存泄漏的原因

基本上,您应该从散列索引开始,将该项与您的键进行比较,然后如果它不等于您的键,则递增到下一个索引并重复,直到找到该项,或者直到遇到一个空桶为止。如果找到该项目,请删除该项目并将该索引标记为已删除。但重要的是,您有一些方法来区分空哈希桶和已删除的哈希桶,否则已删除的哈希桶将导致您提前终止探测序列


至于“良好的面向对象设计”,面向对象编程的固有特性并不一定会使使用
delete
成为糟糕的设计。每个分配内存的数据结构都必须以某种方式释放内存。您可能指的是这样一个事实:实现不管理自己内存的类通常更安全,工作量更少,而是将该职责委托给预先制作的容器类,如
std::vector
std::string

,以获得良好的面向对象设计,集合应该不知道其中存储了什么。这实际上与使用
delete
操作符本身无关,但是需要一个对象(在本例中是您的
stock
)来存储特定于数据结构的代码是非常困难的

我可以看到两个快速解决此问题的计划

  • 使用
    stock*
    数组,而不仅仅是
    stock
    。空值表示插槽打开,非空值表示插槽可以使用。在该计划中,您将在插入整个
    stock
    对象时调用new和delete,然后在删除对象时调用,这比符号更面向对象

  • 创建一个包装
    stock
    项目的
    HashSlot
    类,添加所需的簿记值。 然后,您的哈希表将是一个包含
    HashSlot
    项的数组


  • 我喜欢第二种。在任何一种情况下,
    stock
    都应该有一个析构函数来清除自己的内部内存。

    要获得良好的面向对象设计,集合应该不知道其中存储了什么。这实际上与使用
    delete
    操作符本身无关,但是需要一个对象(在本例中是您的
    stock
    )来存储特定于数据结构的代码是非常困难的

    我可以看到两个快速解决此问题的计划

  • 使用
    stock*
    数组,而不仅仅是
    stock
    。空值表示插槽打开,非空值表示插槽可以使用。在该计划中,您将在插入整个
    stock
    对象时调用new和delete,然后在删除对象时调用,这比符号更面向对象

  • 创建一个包装
    stock
    项目的
    HashSlot
    类,添加所需的簿记值。 然后,您的哈希表将是一个包含
    HashSlot
    项的数组

  • 我喜欢第二种。在任何一种情况下,
    股票
    都应该
    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;
    
    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; }
    }