Thread: Vector class throws bad_alloc

  1. #1
    Registered User
    Join Date
    Sep 2002
    Posts
    14

    Vector class throws bad_alloc

    Hi,

    I'm working on a project for one of my classes and I was trying to use the std::vector class to store pointers to a base class DualObject that will be used to store different types of objects like CircleDual, LineDual, etc. which all inherit from DualObject. Most of them work fine, but whenever I try to add the CircleDual object to this class it sends a bad_alloc. Seeing that they are all the same base class, this shouldn't happen. I then tried it again with the std::list class but ran into the same problem. Below are the portions of the code that seem relevant to me.

    Portion of code from my mouse event handler. The addition of a PointDual * works fine, but the CircleDual * does not:

    Code:
    vector<DualObject *> *objectList = NULL;
    objectList = new std::vector<DualObject *>();
    Code:
    	/* Create a point */
    if ( oldMouseX == x && oldMouseY == y )
    {
    	PointDual *newPD;
    	try
    	{
    		newPD = new PointDual(translateX(x), translateY(y), 20.0, 20.0);
    		newPD->setColor(0.5, 1.0, 0.0);
    
    		/* Add it to the list. */
    		objectList->push_back(newPD);
    	}
    	catch (exception &e)
    	{
    		cerr << e.what() << " at line " << __LINE__ << endl;
    	}
    	catch(...)
    	{
    		cerr << "Error occurred, not sure what it was. (Line: " 
    			<< __LINE__ << ")\n" << endl;
    	}
    	
    }
    else if ( button == GLUT_LEFT_BUTTON 
    			&& keyModifiers == GLUT_ACTIVE_SHIFT ) /* Draw a circle. */
    {
    	CircleDual *newCircle;
    
    	try
    	{
    		newCircle = new CircleDual(translateX(oldMouseX), translateY(oldMouseY),
    			sqrt(pow(translateX(x) - translateX(oldMouseX), 2.0) + 
    			pow(translateY(y) - translateY(oldMouseY), 2.0)), 20.0, 20.0);
    
    		/* Add it to the list. */
    		objectList->push_back((DualObject *)newCircle);
    	}
    	catch (exception &e)
    	{
    		cerr << e.what() << " at line " << __LINE__ << endl;
    
    	}
    	catch(...)
    	{
    		cerr << "Error occurred, not sure what it was. (Line: " 
    			<< __LINE__ << ")\n" << endl;
    	}
    }
    Here are the PointDual and CircleDual Definitions:

    Code:
    class PointDual: public DualObject {
    
    private:
    	double _x, _y;
        double x0, y0, x1, y1;
    
    public:
        /**
         * Default constructor. This creates a PointDual that has a point at
         * location (x, y) in real world coordinates.
         *
         * @params x The x coordinate of the point.
         * @params y The y coordinate of the point.
         * @params width The width of the window.
         * @params height The height of the window.
         */
        PointDual(double x, double y, double width, double height)
        {
    	    x0 = -width / 2.0;
    	    x1 = width / 2.0;
    	    y0 = x * x0 + y;
    	    y1 = x * x1 + y;
    
    	    _x = x;
    	    _y = y;
    
    	}
        
        /**
         * Draw the object normally. This draws the object as described in
         * the regular coordinate system. The base class (this) does nothing.
         */
        virtual void draw()
        {
    		glColor3fv(color);
    		glBegin(GL_POINTS);
    			glVertex2f(_x, _y);
    		glEnd();
        }
        
        /**
         * Draw the object's dual. This draws the object's dual as described
         * by the given extended class. The base class (this) does nothing.
         */
        virtual void drawDual()
        {
    		glColor3fv(color);
    		glBegin(GL_LINES);
    			glVertex2f(x0, y0);
    			glVertex2f(x1, y1);
    		glEnd();
        }
    };
    
    class CircleDual : public DualObject
    {
    private:
    	double centerX, centerY;
    	double radius;
    	double width, height;
    
    	static const int numberOfSegments = 32;
    	double *segmentList;
    
        void drawPointDual(double x, double y)
        {
    	    double x0 = -width / 2.0;
    	    double x1 = width / 2.0;
    	    double y0 = (double)x * x0 + (double)y;
    	    double y1 = (double)x * x1 + (double)y;
    
    		glColor3fv(color);
    		glBegin(GL_LINES);
    			glVertex2f(x0, y0);
    			glVertex2f(x1, y1);
    		glEnd();
        }
    
    public:
    
    	CircleDual()
    	{
    		centerX = centerY = radius = 0.0;
    
    		/* Don't create a segment list. */
    		segmentList = NULL;
    	}
    
    	CircleDual(double x, double y, double r, double width, double height)
    	{
    		/* Copy the circle parameters. */
    		centerX = x;
    		centerY = y;
    		radius = r;
    		segmentList = NULL;
    
    		this->width = width;
    		this->height = height;
    
    		segmentList = new double[numberOfSegments];
    
    		for (int i = 0; i < numberOfSegments; i += 2)
    		{
    			double x0, y0;
    
    			x0 = radius * cos(i * 2.0 * PI / numberOfSegments);
    			y0 = radius * sin(i * 2.0 * PI / numberOfSegments);
    
    			segmentList[2*i] = x0 + centerX;
    			segmentList[2*i + 1] = y0 + centerY;
    		}
    	}
    
    	~CircleDual()
    	{
    		if ( segmentList )
    			delete [] segmentList;
    	}
    
    	/**
    	 * Draw the circle normally using a sequence of line segments.
    	 */
    	virtual void draw()
    	{
    		glBegin(GL_LINE_LOOP);
    		for (int i = 0; i < numberOfSegments; i += 2)
    		{
    			glVertex2d(segmentList[2*i], segmentList[2*i + 1]);
    		}
    		glEnd();
    	}
    
    	/**
    	 * Draw the dual of the circle. For now, just draw the dual of the line segments.
    	 */
    	virtual void drawDual()
    	{
    		for (int i = 0; i < numberOfSegments; i += 2)
    			drawPointDual(segmentList[2*i], segmentList[2*i + 1]);
    	}
    
    };
    Another interesting thing to note is once I catch the bad_alloc, any other actions on std::vector<DualObject *>objectList throws a bad alloc as well. I'm not very familiar with the STL so perhaps I'm forgetting to do something before exiting the exception handler.

    If anyone has any idea of what I may be doing wrong it would be greatly appreciated.

  2. #2
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    I don't think this is enough code to figure out what's going on. What does the base class look like? My first guess is that you're corrupting memory and new[] is failing down inside the vector. If you compile for release instead of debug, does it still happen? Does it crash?

  3. #3
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    You do seem to be adding by pointer to the vector but you should still either implement the copy constructor and assignment operator for CircleDual, or disable them anyway, to help prevent problems at any stage.
    I also recommend learning about and using constructor initialisation lists.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  4. #4
    Registered User
    Join Date
    Sep 2002
    Posts
    14
    I just ran it in both debug and release mode. Playing around with the interface for a few seconds, when I draw the circle first in Debug mode it creates it and adds it but immediately after it throws a bad_alloc and nothing else can be created. If I do the same in release mode, it crashes with a corrupted stack. I've attached the code and header files for anyone that would like to take a look at it.

    you should still either implement the copy constructor and assignment operator for CircleDual, or disable them anyway, to help prevent problems at any stage.
    Is this because the vector could be creating a copy of the actual class pointed to by the pointer? Also, I started looking into the initialization lists, thanks for the tip.

    Here's the code for LineSegmentDual as well since I couldn't attach all of the files (since zipped files aren't allowed)

    Code:
    #ifndef _LINE_SEGMENT_DUAL_H_
    #define _LINE_SEGMENT_DUAL_H_
    
    #include "DualObject.h"
    
    const int LINE_SEGMENT_DUAL = 3;
    /**
     * This class will be used to find the dual of an object. It will act
     * as a base class to all other objects that the dual can be take for.
     * 
     * @author Matthew Knowles
     */
    class LineSegmentDual : public DualObject {
    
    private:
        bool noSlope;
        double slope, intercept;
    	double _x0;
    	double _y0;
    	double _x1;
    	double _y1;
    	double width;
    	double height;
    
    public:
        /**
         * Default constructor. This creates a line of length 0 from (0,0)->(0,0)
         */
        LineSegmentDual()
        {
    
    		_x0 = _x1 = _y0 = _y1 = 0.0;
    	    noSlope = true;
    	    slope = 0.0;
    	    intercept = 0.0;
    
    		type = LINE_SEGMENT_DUAL;
        }
    
        /**
         * Default constructor. This creates a line with specified start and
         * endpoints.
         */
        LineSegmentDual(double x0, double y0, double x1, double y1, double width, double height)
        {
    	    _x0 = (double)x0;
    	    _y0 = (double)y0;
    	    _x1 = (double)x1;
    	    _y1 = (double)y1;
    
    		this->width = width;
    		this->height = height;
    
    	    /* Calculate the slope of the line and its intercept
    	     * so that we can extend the line to the ends of the
    	     * screen.
    	     */
    
    	    if ( x0 == x1 )
    	    {
    		    noSlope = true;
    		    slope = 0.0;
    		    intercept = 0.0;
    	    }
    	    else
    	    {
    		    noSlope = false;
    		    slope = (_y1 - _y0) / (_x1 - _x0);
    		    intercept = -_x0 * slope + _y0;
    	    }
    
    		type = LINE_SEGMENT_DUAL;
        }
        
        /**
         * Draw the object normally. This draws the object as described in
         * the regular coordinate system. The base class (this) does nothing.
         */
        virtual void draw()
        {
    		glColor3fv(color);
    	    glBegin(GL_LINES);
    			glVertex2f(_x0, _y0);
    			glVertex2f(_x1, _y1);
    		glEnd();
        }
        
        /**
         * Draw the object's dual. This draws the object's dual as described
         * by the given extended class. The base class (this) does nothing.
    
         */
        virtual void drawDual()
        {
    	    if ( noSlope )
    	    {
    		    /* Should draw improper points of the form:
    		     * y = mx + 0, y = mx + y, ... where m is
    		     * the x coordinate of the original line.
    		     * For our purposes just draw three.
    		     */
    	    }
    	    else
    	    {
    			/* Draw the two wedge lines. */
    		    drawPointDual(_x0, _y0);
    			drawPointDual(_x1, _y1);
    	    }
        }
    
    private:
        void drawPointDual(double x, double y)
        {
    	    double x0 = -width / 2.0;
    	    double x1 = width / 2.0;
    	    double y0 = (double)x * x0 + (double)y;
    	    double y1 = (double)x * x1 + (double)y;
    
    		glColor3fv(color);
    		glBegin(GL_LINES);
    			glVertex2f(x0, y0);
    			glVertex2f(x1, y1);
    		glEnd();
        }
    };
    
    #endif

  5. #5
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    If you open Task Manager and check how much memory your program is using just before it throws a bad_alloc, and also check how much free memory you have available, you'll probably find you have a huge memory leak somewhere.
    Make sure you're deleting any memory you allocate, once you're finished using it.
    Make sure your copy constructors do a deep-copy.

  6. #6
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    Quote Originally Posted by animeaholic View Post
    I Is this because the vector could be creating a copy of the actual class pointed to by the pointer?
    Not in this case since it is a vector of pointers. It is generally desireable to use a vector of objects, but you can't use polymorphism if you use that so you have to stick with the way you are doing it now.
    It's just safer in case you use anything later that might unintentionally copy the object.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  7. #7
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    If you get a corrupted stack, most likely, it means you've done buffer overruns. The problem might just be that you're invoking undefined behaviour somewhere. I'd suggest trying to fix them first and see if it solves the problem.
    Going about fixing them might vary from IDE and compiler. But any (most) debuggers should be able to catch such errors.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  8. #8
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    Here's one big no-no:
    Code:
    		if ( temp != (DualObject *)NULL )
    		{
    			switch ( temp->getType() )
    			{
    				case DUAL_OBJECT:
    				{
    					delete temp;
    					break;
    				}
    				case POINT_DUAL:
    				{
    					PointDual *pd = dynamic_cast<PointDual*>(temp);
    					delete pd;
    					break;
    				}
    				case LINE_DUAL:
    				{
    					LineDual *ld = dynamic_cast<LineDual*>(temp);
    					delete ld;
    					break;
    				}
    				case LINE_SEGMENT_DUAL:
    				{
    					LineSegmentDual *lsd = dynamic_cast<LineSegmentDual*>(temp);
    					delete lsd;
    					break;
    				}
    				case CIRCLE_DUAL:
    				{
    					CircleDual *cd = dynamic_cast<CircleDual*>(temp);
    					delete cd;
    					break;
    				}
    			}
    		}
    Simply make the destructor virtual and this will ensure that the correct one gets called when you replace ALL of the above with just a single delete temp; line. It could be simplified to the point where the cleanup function is a one-liner if you used a boost ptr_container.

    This dynamic_cast in "mouse" is also totally unnecessary:
    Code:
    				objectList->push_back(dynamic_cast<DualObject*>(newCircle));
    Why is objectList unnecessarily a pointer to a vector instead of just a vector? An empty vector is only the size of 3 pointers.
    Since numberOfSegments is also a compile time value, I'd also suggest just using a fixed size array for segmentList instead of involving dynamic memory allocation there too. That also saves having to write or disable the copy-constructor etc as they will already do the right thing.

    In general you need to reduce the number of dynamic casts (or stop using them entirely) and do less manual memory management. You shouldn't need a getType function either.
    Making changes such as these will mean that there are far less places that you could be corrupting the heap.

    I also hope that those catch(...) statements are only temporary whilst you track down the problem. You should ideally only have one catch(...) in the whole program (in main) and it would be responsible for taking final action such as logging the error before terminating the program. Otherwise all you do is start hiding bugs.
    Last edited by iMalc; 04-28-2008 at 02:05 AM.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. compiler throws error due to the use of "SEEK_SET"
    By yougene in forum C Programming
    Replies: 4
    Last Post: 12-26-2008, 03:53 PM
  2. program throws assert after deleting a resource
    By hanhao in forum C++ Programming
    Replies: 3
    Last Post: 04-04-2007, 09:57 PM
  3. File Opening Throws a Illegal Op.
    By Crossbow in forum C++ Programming
    Replies: 7
    Last Post: 08-11-2004, 03:17 PM
  4. what throws this warning?
    By rox in forum C++ Programming
    Replies: 0
    Last Post: 04-04-2004, 01:06 PM