【问题标题】:Seemingly random program failure at end of completion C++ [closed]完成C ++结束时看似随机的程序失败[关闭]
【发布时间】:2014-02-18 22:32:50
【问题描述】:

警告:我知道如果有人现在可能想停止阅读,这篇文章包含 6 个文件的约 275 行代码(虽然没什么复​​杂的)!我意识到这通常是一件坏事,但这是最后的努力,因为我已经把 cout 放在了所有方法中,显示它们没有崩溃或做任何我没想到的事情,研究了我正在研究的标准方法的实现使用并使用大量输入运行此代码,但有时它会成功运行,有时会失败(完成所有操作后)。我找不到任何模式或损坏的代码段。

该程序模拟了一种具有单个服务器的商店,允许客户订购两种东西中的一种,并且有一条等候线。用户输入模拟长度、每单位时间(分钟)的顾客到达概率,以及制作每件商品所需的时间。运行后,程序会打印出一些统计数据 - 总等待时间(不包括那些仍在排队的人)、服务的客户总数和平均等待时间。

即使进行了长时间的模拟(100,000 分钟),我也看到了成功和失败的运行(同样,只有在模拟完成后才会失败)。起初我认为使用 (>= 1) 来表示到达概率(客户每分钟到达)总是有效的,但后来注意到那些失败了。如果有的话,似乎相当高的到达概率 (> ~.8) 和非常低的 (

当它确实崩溃时,调试器会显示程序计数器在 queueType 的析构函数的右大括号处停止,但是这个析构函数对我来说似乎非常标准,并且相同的语法也适用于其他分配的类堆上的内存与他们的构造函数?我觉得答案一定是一些相当基本的东西,我一直在想办法。

任何帮助将不胜感激,代码如下:

queueType.h:

#ifndef QUEUETYPE_H
#define QUEUETYPE_H
#include <algorithm>
#include <cstdlib>

template<class Type>
class QueueType {
public:
    QueueType();
    ~QueueType();
    QueueType(const QueueType& other);
    Type& getFront() {return queueArray[front];}
    int getNumElements() const {return numElements;}
    void reposition();
    void addElement(Type);
    bool isEmpty() const {return numElements == 0;}
    bool isFull() const {return SIZE == numElements;}
    void updateWaitTimes(Type*&, int&, int&);

    QueueType<Type>& operator=(const QueueType other);

    friend void swap(QueueType& first, QueueType& second) {
        using std::swap;
        swap(first.front, second.front);
        swap(first.back, second.back);
        swap(first.numElements, second.numElements);
        swap(first.queueArray, second.queueArray);
    }
private:
    static const int SIZE = 25;
    int front, back, numElements;
    Type *queueArray;
};

template<class Type>
QueueType<Type>::QueueType() {
    queueArray = new Type[SIZE];
    front = back = numElements = 0;
}

template<class Type>
QueueType<Type>::~QueueType() {
    delete [] queueArray;
}

template<class Type>
QueueType<Type>::QueueType(const QueueType& other):
        queueArray(new Type[SIZE]),
        front(other.front),
        back(other.back),
        numElements(other.numElements)
{
    std::copy(other.queueArray, other.queueArray + SIZE, queueArray);
}

template<class Type>
void QueueType<Type>::reposition() {
    front = (front + 1) % SIZE;
    back = (back + 1) % SIZE;
    numElements--;
}

template<class Type>
void QueueType<Type>::addElement(Type newElement) {
    if (isEmpty()) {
        queueArray[0] = newElement;
        front = back = 0;
        numElements = 1;
    } else {
        back = (back - 1) % SIZE;
        queueArray[back] = newElement;
        numElements++;
    }
}

template<class Type>
void QueueType<Type>::updateWaitTimes(Type*& element, int& position, int& counter) {
    if (isEmpty()) {
        element = NULL;
    } else {
        if (position == 0) {
            position = front;
        }
        element = &queueArray[position];
        position = (position + 1) % SIZE;
    }
    if (counter == numElements) {
        element = NULL;
    }
    counter++;
}

template<class Type>
QueueType<Type>& QueueType<Type>::operator=(const QueueType other) {
    swap(*this, other);
    return *this;
}
#endif  /* QUEUETYPE_H */

customerType.h:

#ifndef CUSTOMERTYPE_H
#define CUSTOMERTYPE_H

class CustomerType {
public:
    CustomerType();
    CustomerType(int, int);
    ~CustomerType();
    CustomerType(const CustomerType& other);
    void incrementWaitTime() {waitTime++;}
    int getArrivalTime() const {return arrivalTime;}
    int getWaitTime() const {return waitTime;}

    CustomerType& operator=(const CustomerType& other);
private:
    int ID, arrivalTime, waitTime;
};

#endif  /* CUSTOMERTYPE_H */

customerType.cpp:

#include "customerType.h"

CustomerType::CustomerType() {
    waitTime = arrivalTime = ID = 0;
}

CustomerType::CustomerType(int arrivalTime, int ID) {
    this->arrivalTime = arrivalTime;
    this->ID = ID;
    waitTime = 0;
}

CustomerType::~CustomerType() {
}

CustomerType::CustomerType(const CustomerType& other) {
    waitTime = other.waitTime;
    arrivalTime = other.arrivalTime;
    ID = other.ID;
}

CustomerType& CustomerType::operator=(const CustomerType& other) {
    waitTime = other.waitTime;
    arrivalTime = other.arrivalTime;
    ID = other.ID;
    return *this;
}

serverType.h:

#ifndef SERVERTYPE_H
#define SERVERTYPE_H

#include "customerType.h"
#include <cstdlib>
#include <string>

class serverType {
public:
    serverType();
    ~serverType();
    serverType(const serverType& other);
    bool isFree() const {return (status == "free");}
    void setCustomer(CustomerType& newCustomer, int& transactionTime);
    void decrementTransactionTime();

    serverType& operator=(const serverType& other);
private:
    std::string status;
    int transactionTime;
    CustomerType currentCustomer;
};

#endif  /* SERVERTYPE_H */

serverType.cpp:

#include "serverType.h"

serverType::serverType() {
    status = "free";
    transactionTime = 0;
}

serverType::~serverType() {
}

serverType::serverType(const serverType& other) {
    status = other.status;
    transactionTime = other.transactionTime;
    currentCustomer = other.currentCustomer;

}

void serverType::setCustomer(CustomerType& newCustomer, int& transactionTime) {
    currentCustomer = newCustomer;
    this->transactionTime = transactionTime;
    status = "busy";
}

void serverType::decrementTransactionTime() {
    transactionTime--;
    if (transactionTime == 0)
        status = "free";
}

serverType& serverType::operator=(const serverType& other) {
    status = other.status;
    transactionTime = other.transactionTime;
    currentCustomer = other.currentCustomer;
    return *this;
}

ma​​in.cpp:

#include "queueType.h"
#include "serverType.h"
#include <ctime>
#include <climits>
#include <iostream>
using namespace std;

int main(int argc, char** argv) {

    int simulationTime, coneTime, shakeTime, currentTime = 0;
    int customerID = 1, totalWaitTime = 0, customersServiced = 0;
    double arrivalProb;

    cout << "Time-driven ice cream shop simulation" << endl
            << "Enter the following information to begin:" << endl << endl;
    cout << "Length of simulation (in minutes): ";
    cin >> simulationTime;
    cout << endl << "Probability of customer arrival each minute (example: 0.25): ";
    cin >> arrivalProb;
    cout << endl << "Minutes to make an ice cream cone: ";
    cin >> coneTime;
    cout << endl << "Minutes to make a shake: ";
    cin >> shakeTime;
    cout << endl << endl;

    QueueType<CustomerType> Line;
    serverType server;
    float chance;
    srand(time(0) % INT_MAX);

    while (currentTime < simulationTime) {
        chance = float (rand())/RAND_MAX;
        if (chance < arrivalProb) {
            if (!Line.isFull()) {
                Line.addElement(CustomerType(currentTime, customerID));
                customerID++;
            } else {
                cout << "Customer #" << customerID 
                        << " came during a full line and left!" << endl;
                customerID++;
            }
        }
        if (server.isFree() && (!Line.isEmpty())) { //going with 40% shake, 60% cone
            customersServiced++;
            if (chance < 0.4) {
                server.setCustomer(Line.getFront(), shakeTime);
            } else {
                server.setCustomer(Line.getFront(), coneTime);
            }
            totalWaitTime += Line.getFront().getWaitTime();
            Line.reposition();
        } else if (!server.isFree()) {
            server.decrementTransactionTime();
            CustomerType *customerPointer = new CustomerType();
            int position = 0, counter = 0;
            Line.updateWaitTimes(customerPointer, position, counter);
            while (customerPointer != NULL) {
                (*customerPointer).incrementWaitTime();
                Line.updateWaitTimes(customerPointer, position, counter);
            }
            delete customerPointer;
        }
        currentTime++;
    }
    cout << endl << endl << "Simulation complete." << endl << endl;
    cout << "Total wait time: " << totalWaitTime << endl
            << "Customers serviced: " << customersServiced << endl
            << "Average wait time: " << float (totalWaitTime) / customersServiced;
    return 0;
}

请注意,queueType 复制构造函数/重载 =/destructor 永远不会被调用,直到最后析构函数执行一次。我也知道我不需要将 customerType (currentCustomer) 作为 serverType 的私有成员之一,但只是为了现实。

【问题讨论】:

  • 请做一个简短的独立示例。这样做,您很可能会发现问题所在。 sscce.org
  • 哇。 1)如何给我们你用来重现问题的数据而不是 cin 语句。 2) 您对 QueueType 类的单元测试做得如何?在这样的程序中使用它需要它实际上是防弹的。
  • 1) 我尝试过 10 -> 100,000 次(通常每个数量级 1 次),到达概率从 0 到 1(0, 0.01, 0.02, 0.1, 0.2, 0.5, 0.75, 0.8 , 0.9, 0.99, 1) 和从 1, 1 到 4, 4 所有情况下的摇晃/圆锥时间 2) 我主要在更改任何变量的方法中使用 cout,向我展示它们正在以我想的方式改变,想要一个循环数组
  • @Cogwheel - 除了使用非常小的输入之外,不确定如何制作一个使用每个类的更短示例? (我试过很多次了)

标签: c++ templates pointers dynamic-arrays


【解决方案1】:

你在这里管理不善:

CustomerType *customerPointer = new CustomerType();
int position = 0, counter = 0;
Line.updateWaitTimes(customerPointer, position, counter);

您正在为 customerPointer 分配内存。然后在 Line.updateWaitTimes 函数中更改 customerPointer 指向的值。然后你这样做:

delete customerPointer;

因此,您分配的内容和删除的内容具有不同的值。您通过尝试删除不是从动态分配块的开头开始的地址来破坏堆。

如果您要删除的一个指向动态分配内存的指针,即您以这种方式设计它,但与您开始使用的原始指针“不同”,那么您需要重写您的代码,因此您不会在 customerPointer 和 Line.updateWaitTimes 函数之间进行这种“指针舞蹈”。

这可能只是您需要修复的代码的许多问题之一。一种解决方法是在 main() 函数中使用手动内存管理退出。学习编写代码以最小化或消除原始裸指针的使用。是的,您的 QueueType 类必须进行内存管理,当然,但这并不意味着您的 main() 也必须这样做。

此外,您的 QueueType 类维护自己的内存,不应被外部实体愚弄。看看你的 QueueType::updateWaitTimes 函数——为什么它给出一个指向传入的“元素”指针的指针?然后,您使用此指针指向您的内部队列并在 main() 中对其进行处理,这会产生灾难性的结果。像这样写代码意味着你还没有完全理解“封装”的含义。

【讨论】:

  • 哦,我以为这是释放为该指针分配的内容的正确方法。有没有办法通过简单的修复来完成这个过程?我不会手动管理 main 中的内存,但我的目标是让 queueType 完全可重用,并且不必了解有关 customerType 的任何信息,因此它对 updateWaitTimes 函数所做的所有事情就是将指针移动到下一个元素,直到它覆盖了所有内容它的数组,然后我会在 main.js 中更新每个客户。为什么只更改指针包含的地址 (element = &queueArray[position];) 会改变它的任何内容?
  • 没有。阅读我更新的评论。您应该永远让外部实体负责您的 QueueType 类如何处理内存。
  • 用“new”分配时,会返回一个值。见鬼或高水位,您最好保留该价值。原因是在调用“删除”时您将需要相同的值。请注意,我说的是“价值”而不是“指针”。
  • 您的 QueueType 应该是通用的。我认为您正在尝试做的是采用 QueueType 并赋予它了解您的 main() 程序正在做什么的一切的能力。这是错误的。 QueueType() 有一个公共接口——任何使用 QueueType 的函数都有一个公共接口来处理而没有别的。一项更改是从 QueueType 中删除“updateWaitTimes”函数——它根本不应该存在。
  • 哦,好吧,如果你必须保留一个值,这并不意味着你永远不应该使用:pointerName = &somethingElse 在使用pointerName分配堆空间之后?是否有任何不同的方法来更新 queueType 数组中每个客户的等待时间,既不使用 main 中的指针,又不允许 queueType 直接访问 customerTypes?
【解决方案2】:

这条线很可能是问题,因为它可能会留下负面影响

back = (back - 1) % SIZE;

你的意思可能是这样的

back = (SIZE + back - 1) % SIZE;

【讨论】:

  • 它似乎必须始终为正,最小的情况是从 0 开始,所以 back = (0 - 1) % SIZE 仍然会给出 24?
  • 但是 SIZE 是一个静态常量 int 设置为 25,所以 (0 - 1) % 25: google.com/#q=-1+modulo+25
  • 这取决于 C++ 是如何定义它的:我建议你试试这段代码:std::cout
  • 哦,这很奇怪,不幸的是改为 back = (SIZE + back - 1) % SIZE;仍然会导致崩溃,但谢谢我不知道 C++ 以这种方式使用模
  • 让我想,这是问题的一部分,但它应该是 back = (back + 1) % SIZE;我不知道为什么要向后移动它,谢谢您的帮助
【解决方案3】:

哇。终于意识到它崩溃的原因是我如何在 queueType::reposition 和 queueType::addElement 中变回,在重新定位中我根本不需要向后移动,因为它只是在有人离开前面后调用,在我的添加我的意思是向后移动一个,但使用了 - 而不是 + 并将其向前移动......程序已修复。谢谢你的回答/cmets

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-16
    • 1970-01-01
    • 2010-11-05
    • 2016-12-15
    • 1970-01-01
    • 2020-06-07
    • 2019-04-08
    • 1970-01-01
    相关资源
    最近更新 更多