在C ++中安全删除

时间:2009-11-21 00:59:11

标签: c++

我开发了一个基于数组的hashTable实现,它具有多个股票名称,符号,价格等。我需要从我的阵列中删除一个股票。我被告知使用删除操作符是错误的面向对象设计。什么是良好的面向对象设计删除?

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 = &hashTable[usedIndex];
delete s.symbol;
s.symbol = NULL;
hashTable[usedIndex] = &s;

这确实有效,但会导致内存泄漏。即便如此,我也不确定它是否是好的物体设计。

这是我的标题,其中库存和所有内容都已初始化和定义。

// 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

};

// hashtable ctor

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; &lt; - 没有这样做。

阵列中插槽的状态(空等)不应记录在库存实例中。这是糟糕的面向对象设计。相反,我需要将数组插槽的状态存储在数组插槽中。

我该怎么做?

4 个答案:

答案 0 :(得分: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 :(得分:1)

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

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

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

答案 2 :(得分:1)

为了获得良好的面向对象设计,集合应该与存储在其中的内容无关。这实际上与使用delete运算符无关,但需要一个对象(在这种情况下为stock)来存储特定于数据结构的代码。

我可以看到有两个计划可以快速解决这个问题。

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

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

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

答案 3 :(得分:1)

  

我开发了一个基于数组的hashTable实现,它具有多个股票名称,符号,价格等。我需要从我的阵列中删除一个股票。我被告知使用删除操作符是错误的面向对象设计。什么是良好的面向对象设计删除?

嗯,面向对象设计背后的关键原则之一是可重用性

因此,唯一良好的面向对象设计是重用已经为您开发的解决方案!

C ++带有一个完美的map类。最新的编译器也支持TR1,它在名称unordered_map下添加了一个哈希表。

如果您遇到没有TR1支持的编译器,Boost库还包含unordered_map的实现。

至于您关于delete的问题:
我不确定是谁告诉你delete是“糟糕的面向对象设计”,或者为什么,但他们可能意味着它是糟糕的C ++设计< / em>的

一个共同的指导原则是你永远不应该明确地呼叫delete。相反,它应该通过使用 RAII 成语隐式调用。

每当你创建一个必须在以后删除的资源时,你将它包装在一个小的堆栈分配对象中,其析构函数为你调用delete

这可以保证在RAII对象超出范围时删除它,无论 你离开范围如何。即使抛出异常,对象仍会被清理,其析构函数被调用,并且您的资源被删除。如果您需要更复杂的方法来管理对象的生命周期,您可能希望使用智能指针,或者只是使用复制构造函数和赋值运算符扩展RAII包装器,以允许复制或移动资源的所有权。

这是很好的C ++实践,但是没有与面向对象的设计有关。并非一切都如此。 OOP不是编程的圣杯,而不是所有都必须是OOP。好的设计比好的OOP重要得多。