我正在尝试重新创建我在C#中用C ++创建的优先级队列实现,作为一个跳转到C ++的项目,但很多细微差别让我感到沮丧。该队列被设计为一个模板,可以处理任何给定的类T.该队列将显式使用一个表示名为Priority Pairs的对象的结构:一个指向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;
PriorityPair的结构:
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):
第一个析构函数调用正确地显示了字符串及其值,但在第二个中我们可以看到字符串本身已超出范围(这很好并且是预期的)。
答案 0 :(得分:2)
除了以下划线开头的名称,正如人们在评论中指出的那样,您的问题似乎在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 ++时你可能会觉得有用(不是一个全面的列表;我主要不是在寻找这些):
new
表达式可以进入初始化列表中,我认为我所谈到的所有人都是第一次问这个或假设不正确,包括我)。这将更有效,更具惯用性。PriorityPair
实现析构函数(除了学习语言的细微差别);它就是所谓的普通旧数据(POD)类型。如果您希望将PriorityPair
的{{1}}破坏为delete
,那么您需要T
,但您希望T
完全单独管理。PriorityQueue
的析构函数逻辑一样正确,并且很容易弄乱那种东西。恭喜!