Reputation: 23
I'm working on an assignment in which we are manipulating *.ppm images using a "Image" class. It compiles and then seg faults when the copy constructor is called. Here it is:
Image::Image(const Image & imageToCopy) {
fileType = imageToCopy.fileType;
width = imageToCopy.width;
height = imageToCopy.height;
maxColor = imageToCopy.maxColor;
image = new int *[height];
for(int i=0; i < height; i++) {
image[i] = new int [width];
for(int j=0; j < width; j++) {
image[i][j] = imageToCopy.image[i][j];
}
}
}
Called like so:
Image image2(image1);
I'm at somewhat of a loss for why this happens. I don't know what is wrong because the code is almost identical to my constructor, which works fine. the only difference is that I have
image[i][j] = imageToCopy.image[i][j];
instead of
imageInputStream >> image[i][j];
Thoughts? Thanks
EDIT: Constructor is below:
Image::Image(const char* filename) {
ifstream imageInputStream;
imageInputStream.open(filename);
imageInputStream >> fileType;
imageInputStream >> width;
imageInputStream >> height;
imageInputStream >> maxColor;
image = new int *[height];
for(int i=0; i < height; i++) {
image[i] = new int [width];
for(int j=0; j < width; j++) {
imageInputStream >> image[i][j];
}
}
imageInputStream.close();
}
Upvotes: 2
Views: 5658
Reputation: 365845
I took the code you pasted at pastebin and added some logging to the two constructors.
In Image(const char *)
, right after imageInputStream >> maxColor
;:
cout << "Image(" << filename << "): "
<< fileType << ", "
<< width << ", "
<< height << ", "
<< maxColor << endl;
In Image(const Image&)
, right after maxColor = imageToCopy.maxColor;
:
cout << "Image(Image): "
<< fileType << ", "
<< width << ", "
<< height << ", "
<< maxColor << endl;
And here's the output:
Testing standard constructor, opening tux.ppm
Image(tux.ppm): P3, 0, 0, 0
Calling flip method
Calling dither method
Saving the flipped and dithered image
Creating a 2nd image object, using tux.ppm again with the standard constructor
Image(tux.ppm): P3, 32767, 1656563112, 17
In other words, you're reading garbage. The first time through, I happen to read 0x0 and return an empty image. Then I read 32767x1656563112 and create a huge garbage image, which takes so long that I don't want to wait around, but I could imagine that copying it would segfault.
And to verify that this is the case, look at what gets output as tux_transformed.ppm:
P3
0 0
0
Of course I don't have your tux.ppm file, but I threw a bunch of sample PPMs at it from different sources, and they all had the same problem.
As a wild guess, your problem is that you're not handling PPM comments, so you're trying to read one as, e.g., a width. But it really doesn't matter. If your code can create garbage objects, it can crash trying to copy them.
Upvotes: 0
Reputation: 365845
Without seeing the complete code, this is just a guess, but if you've created a copy constructor and a destructor but no copy assignment operator, you may get a segfault exactly like this if you try to use assignment.
And you may not think you're doing an assignment anywhere, but unless you know all the rules of C++ (which even experts don't, much less new students), it's hard to be sure. The easiest way to find out is to declare a private assignment operator and don't define it (or, if you're using C++11, declare it deleted) and see if you get a compile error.
For example:
struct Image {
int width_, height_;
int **image_;
Image(int width, int height) : width_(width), height_(height),
image_(0) {
image_ = new int *[height_];
for (int i = 0; i != height_; ++i) {
image_[i] = new int[width_];
for (int j = 0; j != width_; ++j) {
image_[i][j] = 1;
}
}
}
Image(const Image& rhs) : width_(rhs.width_), height_(rhs.height_),
image_(0) {
image_ = new int*[height_];
for (int i = 0; i != height_; ++i) {
image_[i] = new int[width_];
for (int j = 0; j != width_; ++j) {
image_[i][j] = rhs.image_[i][j];
}
}
}
/* uncomment to uncrash
Image& operator=(const Image& rhs) {
if (this == &rhs) return *this;
for (int i = 0; i != height_; ++i) {
delete [] image_[i];
}
delete [] image_;
image_ = new int*[height_];
for (int i = 0; i != height_; ++i) {
image_[i] = new int[width_];
for (int j = 0; j != width_; ++j) {
image_[i][j] = rhs.image_[i][j];
}
}
return *this;
}
*/
~Image() {
for (int i = 0; i != height_; ++i) {
delete [] image_[i];
}
delete [] image_;
}
};
int main(int, char*[]) {
Image img(200, 300);
Image img2(img);
Image img3(100, 200);
img3 = img2;
return 0;
}
As the comment implies, if you uncomment the copy assignment operator, the whole thing works.
If this is your problem, read up on the Rule of Three (Wikipedia, Ward's Wiki).
Upvotes: 3