我开发了一个基于数组的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; - 没有这样做。
阵列中插槽的状态(空等)不应记录在库存实例中。这是糟糕的面向对象设计。相反,我需要将数组插槽的状态存储在数组插槽中。
我该怎么做?
答案 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
)来存储特定于数据结构的代码。
我可以看到有两个计划可以快速解决这个问题。
使用stock *
而不仅仅是stock
的数组。然后空值表示插槽打开,非空值表示可以使用插槽。在此计划中,您将在插入整个stock
对象时调用new和delete,然后在删除时调用它,这比面向对象更加面向对象。
创建一个包含HashSlot
项的stock
类,添加所需的簿记值。
然后,您的哈希表将是HashSlot
个项目的数组。
我更喜欢第二个。在任何一种情况下,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重要得多。