我正在尝试重新创建一个用C#编写的优先级队列实现,将C++作为一个要跳入C++的项目,但是很多细微差别使我绊倒了。该队列被设计为可在任何给定类T上工作的模板。该队列将显式地与表示对象(称为优先级对)的结构一起工作:指向T对象的指针和关联的优先级值(int)。
这样做的目的是允许队列中要比较的实际对象(T)完全分开,并且只能指向。我可能并不需要显式的结构来完成此操作,但这就是我正在执行的操作。
队列实现中的重要内容:
template <class T>
class PriorityQueue
{
public:
PriorityQueue(const int maxSizeIn)
{
maxSize = maxSizeIn;
queueArray = new PriorityPair<T>*[maxSize];
currentHeapSize = 0;
}
~PriorityQueue()
{
cout << "Destroy Queue with size: " << currentHeapSize << endl;
for (int i = 0; i < currentHeapSize; i++)
{
delete (PriorityPair<T>*)queueArray[i];
}
delete[] queueArray;
}
private:
PriorityPair<T>** queueArray;
优先配对的结构:
template <class T>
struct PriorityPair
{
PriorityPair(int valueIn, T* objectIn)
{
_PriorityValue = valueIn;
_Object = objectIn;
};
~PriorityPair()
{
cout << "Destroy Pair for object :(" << *_Object << "): << endl;
}
int _PriorityValue;
T* _Object;
};
在测试过程中,我发现调用PeekTop方法似乎导致PriorityPair调用其析构函数。我最好的猜测是,由于不了解某种语言的细微差别,我不小心创建了一个临时语言。
这是偷看方法:
T PeekTop()
{
if (IsEmpty())
return nullptr;
else
return *((PriorityPair<T>)(*queueArray[0]))._Object;
}
此外,这是插入操作(最低有效的插入,不执行堆/队列操作):
int InsertElement(PriorityPair<T>* elementIn)
{
//do not insert nulls --
if (elementIn == nullptr)
return -2;
//we could user std::vector or manually expand the array, but a hard max is probably sufficient
if (currentHeapSize == maxSize)
{
return -1;
}
//insert the pointer to the new pair element in at the index corresponding to the current size, then increment the size
queueArray[currentHeapSize++] = elementIn;
return 0;
}
我主要有以下几点:
PriorityQueue<string> queue = PriorityQueue<string>(10);
string s1 = "string1";
int code = queue.InsertElement(new PriorityPair<string>(5, &s1));
string i = queue.PeekTop();
cout << "-------\n";
cout << i << endl;
在确实正确插入元素的范围内,这似乎可行,但是我不知道该对新连接是否按照我的预期运行。当我运行代码时,优先级对的析构函数被调用两次。这在调用PeekTop函数时特别发生。一次在队列的生存期内,一次在队列超出范围并被销毁。
这是上面代码的输出:
Code: 0
Destroy Pair for object :(string1): with priority :(5):
-------
string1
Destroy Queue with size: 1
Destroy Pair for object :(): with priority :(5):
第一个析构函数调用正确显示了带有其值的字符串,但是在第二个中,我们可以看到字符串本身超出了范围(这是可以预期的)。
最佳答案
正如人们在评论中指出的那样,除了以下划线开头的名称之外,您的问题似乎在return *((PriorityPair<T>)(*queueArray[0]))._Object
行中。让我们从内而外逐一查看,然后逐步解决。queueArray
是PriorityPair<T>**
中声明的PriorityQueue
。这可以理解为“指向PriorityPair<T>
的指针的指针”,但是在您的情况下,您看起来像是“指向PriorityPair<T>
的指针的原始数组”,这也是一个有效的读数。到现在为止还挺好。queueArray[0]
是PriorityPair<T>*&
或“对PriorityPair<T>
的指针的引用”。引用在C++中几乎是看不见的,这仅意味着您要处理的是数组的实际第一个元素,而不是副本。同样,在尝试窥视队列顶部时,这是一个合理的要求。*queueArray[0]
只是PriorityPair<T>&
或“对PriorityPair<T>
的引用”。同样,此处的引用仅表示您正在处理queueArray[0]
指向的实际内容,而不是副本。(PriorityPair<T>)(*queueArray[0])
是PriorityPair<T>
,将您已经拥有的一个转换为一个新的结果。 这将创建一个临时的PriorityPair<T>
,即您稍后看到的销毁的。没有程序性的理由进行这种强制转换(您的IntelliSense问题是一个不同的问题,我对VS的了解不足,无法对此发表评论);它已经是正确的类型。如果将this
添加到输出中,则可以验证它是否已被销毁,因为this
是指向当前对象的指针,并且临时对象需要驻留在内存中的其他位置。((PriorityPair<T>)(*queueArray[0]))._Object
是T*
或“指向T
的指针”。实际上,它指向存储在优先级队列顶部的T
,这很好。
最后,完整表达式*((PriorityPair<T>)(*queueArray[0]))._Object
对其取消引用以得到T
,return语句返回该T
的副本。这不会影响您所看到的行为,但是如果您向测试对象添加了析构函数调用,则会如此。通过将返回类型从T
更改为T
或T&
,返回对T const&
的引用(将放弃复制)可能会更有效。
我注意到的与该问题无关的其他问题,在学习C++时可能会有用(不是完整的 list ;我主要不是在寻找这些问题):
new
表达式可以放入初始化列表中,我认为我所说的每个人实际上都是第一次问这个问题,或者包括我在内都认为是错误的)。这样会更有效,更惯用。 PriorityPair
实现析构函数(除了学习语言的细微差别外);这就是所谓的普通旧数据(POD)类型。如果您希望通过PriorityPair
的销毁来对delete
进行T
,则需要这样做,但是您希望对T
进行完全分开的管理。 PriorityQueue
的析构函数一样,很容易弄乱这种事情。恭喜你!