似乎是随机程序在完成C++时失败

Seemingly random program failure at end of completion C++

本文关键字:C++ 失败 随机 程序 似乎是      更新时间:2023-10-16

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

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

即使是长时间的模拟(100000分钟),我也看到过成功和失败的运行(同样,只有在模拟完成后才失败)。起初,我认为使用(>=1)表示到达概率(客户每分钟到达)似乎总是有效的,但后来我注意到了这些失败。如果有什么不同的话,在长时间模拟中,它似乎是相当高的到达概率(>.8)和非常低的(<=~0.01)到达概率崩溃的最低频率,但有时在短时间模拟中仍然可能发生。很奇怪!

每当发生崩溃时,调试器都会显示程序计数器在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;
}

main.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复制构造函数/重载=/析构函数。我也知道我不需要将customerType(currentCustomer)作为serverType的私有成员之一,但只是为了现实起见。

您在这里管理内存错误:

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()中使用它,这会产生灾难性的结果。编写这样的代码意味着您还没有完全理解"封装"的含义。

这行可能有问题,因为它可能会作为负返回

back = (back - 1) % SIZE;

你的意思可能是

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

哇。我终于意识到它崩溃的原因是我如何在queueType::reserve和queueType::addElement中改回,在reserve中我根本不需要移回,因为它只是在有人离开前台后调用的,在我的添加中,我本想移回一个,但使用了-not+并将其向前移动。。。程序已修复。感谢您的回答/评论