2009-11-21 59 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。好的设计比好的面向对象更重要。