从指针

时间:2017-03-15 22:10:14

标签: c++ pointers return destructor

我正在尝试重新创建我在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):

第一个析构函数调用正确地显示了字符串及其值,但在第二个中我们可以看到字符串本身已超出范围(这很好并且是预期的)。

1 个答案:

答案 0 :(得分:2)

除了以下划线开头的名称,正如人们在评论中指出的那样,您的问题似乎在return *((PriorityPair<T>)(*queueArray[0]))._Object行中。让我们从内到外一块一块地看着它,然后一直在努力。

queueArrayPriorityPair<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]))._ObjectT*或“指向T的指针”。实际上,它指向为优先级队列的顶部存储的T,这很好。

最后,完整表达式*((PriorityPair<T>)(*queueArray[0]))._Object取消引用它以给出T,并且return语句返回副本T。这不会影响您所看到的行为,但如果您将析构函数调用添加到您测试过的对象中,它会。通过将返回类型从T更改为TT&,返回对T const&的引用可能会更有效率,这会放弃复制。

我注意到的其他问题,与这个问题无关,在学习C ++时你可能会觉得有用(不是一个全面的列表;我主要不是在寻找这些):

  • 你的两个构造函数都应该使用initializer lists并且有空体(是的,new表达式可以进入初始化列表中,我认为我所谈到的所有人都是第一次问这个或假设不正确,包括我)。这将更有效,更具惯用性。
  • 您不需要为PriorityPair实现析构函数(除了学习语言的细微差别);它就是所谓的普通旧数据(POD)类型。如果您希望将PriorityPair的{​​{1}}破坏为delete,那么您需要T,但您希望T完全单独管理。
  • 正如人们在评论中指出的那样,you aren’t allowed to use those identifier names yourself以防编译器或标准库需要它们。这个可能没问题,可能在编译时会导致问题,或者可能似乎正常工作但发送所有用户的浏览器给父母和/或雇主的历史和电子邮件。最后不太可能,但C ++标准并不禁止它;这是未定义行为的含义。其他允许的行为正在创造一个黑洞来摧毁地球和shooting demons out of your nose,但这些在实践中的可能性更小。
  • 认为就像你有PriorityQueue的析构函数逻辑一样正确,并且很容易弄乱那种东西。恭喜!