问题
I am attempting to translate a 2 dimensional cellular automata from Processing to openFrameworks (C++). I have written classes for the cells and for the game of life functionality. The application builds successfully but crashes immediately with the following error: Thread 1: Program received signal: "EXC_BAD_ACCESS".
Here's my the header of my game of life class
#include "Cell.h"
class GoL {
public:
GoL();
void init();
void generate();
void display();
void run();
int w = 20;
int cols;
int rows;
std::vector<vector<cell> > board;
};
and here is the implementation:
#include "GoL.h"
GoL::GoL() {
cols = ofGetWidth() / w;
rows = ofGetHeight() / w;
board[rows][cols];
init();
}
void GoL::run() {
generate();
display();
}
void GoL::init() {
for (int i = 0; i < cols; i ++) {
for (int j = 0; j < rows; j ++) {
board[i][j] = *new cell(i * w, j * w, w);
}
}
}
void GoL::generate() {
for (int i = 0; i < cols; i ++) {
for (int j = 0; j < rows; j ++) {
board[i][j].savePrevious();
}
}
for (int x = 0; x < cols; x ++) {
for (int y = 0; y < cols; y ++) {
int neighbours = 0;
for (int i = -1; i <= 1; i ++) {
for (int j = -1; j <= 1; j ++) {
neighbours += board[(x + i + cols) % cols][(y + j + rows) % rows].previous;
}
}
neighbours -= board[x][y].previous;
// Rules of Life
if ((board[x][y].state == 1) && (neighbours < 2)) board[x][y].newState(0);
else if ((board[x][y].state == 1) && (neighbours > 3)) board[x][y].newState(0);
else if ((board[x][y].state == 0) && (neighbours == 3)) board[x][y].newState(1);
}
}
}
void GoL::display() {
for (int i = 0; i < cols; i ++) {
for (int j = 0; j < rows; j ++) {
board[i][j].display();
}
}
}
the error shows up in the vector.h file, in the GoL header file and where I call the init() method in the GoL implementation. Any help is much appreciated.
回答1:
You have an out of bounds access here, because the vector has size 0:
board[rows][cols];
You can initialize the vector in the constructor initialization list like this:
GoL::GoL() : cols(ofGetWidth()/w), rows(ofGetHeight()/w), board(rows, std::vector<cell>(cols))
{
}
That will initialize board
to be size rows
, and each of its elements will be a vector of size cols
. Then you can assign values to its elements:
cell c = ...;
board[i][j] = c;
回答2:
You definitely have an out of bound access due to your uninitialized vector. Technically your vector is initialized, but only as an empty container, you are implying that you should be reserving enough cells to treat it and its contained vector as a 2d array and thus must either a) add cells in a loop b) use the range constructor c) use the constructor that takes a count for the number of elements. More information is available here.
It is best to always make sure your types are usable by the end of the constructor. Prefer to initialize what you can in the constructor initialization list, and fall back on using the constructor scope for anything that requires more logic. If your type cannot always be constructed fully consider the named constructor idiom. Basically you are just returning a handle from a static or non-member friend function allowing you to return a sentinel value for unsuccessful creation (aka NULL for pointers).
It sounds like you need to consider how the C++ type system works as well.
Unless your type "cell" is a handle to some data type or simply a POD type you will likely want to store a reference to a heap allocated object in your vector and not a copy of a cell object.
If cell must be treated like a polymorphic type (you want to use it as a base class) you will need to store some form of handle in your vector such as a pointer or better yet smart pointer.
If your using C++11 you can use one of the new built in smart pointers or you can always fallback on boost.
The key point is that you should prefer to use RAII practices to avoid dangling references. Although C++ does not have a built in garbage collector, you can achieve an incredibly stable product employing RAII principles. The main thing to avoid is circular references which can be mitigated by employing weak references to relations where you do not require the referent to remain alive. A common example of this is when you have an object hierarchy where a parent class holds a reference to children who intern hold a reference to the parent. The children will likely not need a strong reference to the parent as they will be expected to go out of scope when the parent does.
来源:https://stackoverflow.com/questions/15074436/new-to-c-exc-bad-access-error-i-dont-understand