views:

105

answers:

2

First question here!

So, I am having some problems with pointers in Visual C++ 2008. I'm writing a program which will control six cameras and do some processing on them so to clean things up I have created a Camera Manager class. This class handles all operations which will be carried out on all the cameras. Below this is a Camera class which interacts with each individual camera driver and does some basic image processing.

Now, the idea is that when the manager is initialised it creates two cameras and adds them to a vector so that I can access them later. The catch here is that when I create the second camera (camera2) the first camera's destructor is called for some reason, which then disconnects the camera.

Normally I'd assume that the problem is somewhere in the Camera class, but in this case everything works perfectly as long as I don't create the camera2 object.

What's gone wrong?

CameraManager.h:

#include "stdafx.h"

#include <vector>
#include "Camera.h"

class CameraManager{

    std::vector<Camera>     cameras;

public:

    CameraManager();
    ~CameraManager();

    void CaptureAll();
    void ShowAll();

};

CameraManager.cpp:

#include "stdafx.h"

#include "CameraManager.h"


CameraManager::CameraManager()
{

    printf("Camera Manager: Initializing\n");
    [...]
    Camera *camera1 = new Camera(NodeInfo,1, -44,0,0);
    cameras.push_back(*camera1);

    // Adding the following two lines causes camera1's destructor to be called. Why?
    Camera *camera2 = new Camera(NodeInfo,0,  44,0,0);
    cameras.push_back(*camera2);

    printf("Camera Manager: Ready\n");

}

Camera.h

#include "stdafx.h"

// OpenCV
#include <cv.h>
#include <highgui.h>

// cvBlob
#include "cvblob.h"

// FirePackage
#include <fgcamera.h>

using namespace cvb;

class Camera{

public:

    int cameraID;

    double x, y,z, FOVx, FOVy;

    IplImage *image, *backgroundImage, *labeledImage; 

    CvBlobs   blobs;

    Camera(FGNODEINFO NodeInfo[], int camID, float xin, float yin, float zin);

    ~Camera();

    void QueryFrame();

    void ProcessFrame();

    void GrabBackground();

    void LoadCalibration();

    void Show();

private:

    // ======= FirePackage  ======
    CFGCamera  FGCamera;
    UINT32     Result;
    FGNODEINFO MyNodeInfo;
    UINT32     NodeCnt;
    FGFRAME    Frame;

    // ======= Camera Configuration  ======
    // Trigger Settings
    UINT32  nOn, nPolarity, nSrc, nMode, nParm, BurstCount, DMAMode;

    // Image Settings
    UINT32  AutoExposure, Shutter, Gain, Brightness, Gamma;

    // Image Format Settings
    UINT32  Format, Mode, Resolution, ColorFormat, FrameRate;

    // Structures
    UINT32  TriggerValue;
    UINT32  FormatValue;
    UINT32  DFormatValue;

    // OpenCV Calibration matrices
    CvMat    *intrinsics, *distortion;  
    IplImage *mapx, *mapy;

    void SetUpFirePackage();

    void SetUpOpenCV();

};

Camera.cpp:

#include "stdafx.h"

#include "Camera.h"



Camera::Camera(FGNODEINFO NodeInfo[], int camID, float xin, float yin, float zin) 
    {

        cameraID = camID;
        x = xin;
        y = yin;
        z = zin;
        FOVx = 42.6;
        FOVy = 32.5;

        MyNodeInfo = NodeInfo[cameraID];

        SetUpFirePackage();
        SetUpOpenCV();

        // Grab the first frame
        printf("Waiting for frame...\n");
        QueryFrame();

    };

//Destructor
Camera::~Camera()
{   

        // Stop the device
        FGCamera.StopDevice();

        // Close capture
        FGCamera.CloseCapture();

        // Disconnect before ExitModule
        FGCamera.Disconnect();

        // Exit module
        FGExitModule();

        cvReleaseImage(&image);
    };
[...]
};
+8  A: 

You need to get clear the difference between objects and pointers to objects.

Your CameraManager contains a vector of Camera so you must expect your cameras to be copied as the vector expands. This means that copies will be created and the old copies destroyed at certain points in the lifetime of the container.

This call pushes a copy of the parameter (the camera pointed to by camera1) into the vector.

cameras.push_back(*camera1);

When the second Camera is pushed into the vector it is not the Camera pointed to by camera1 that is destroyed but a copy of this Camera that was pushed into the vector. As a side note, you have a memory (and object) leak as camera1 points to an object that you allocated dynamically with new but which you don't delete.

It sounds as though you are not prepared for your Camera objects to be copied. It may be that you are better off with a container of pointers (or smart pointers to help with clean deallocation) or it may be possible for you to alter the way your Camera class works to cope correctly with being copied. Without seeing the Camera class it is difficult to know which is more appropriate.

Charles Bailey
That was a quick answer! I'd prefer to use pointers as destroying objects will cause the cameras to be disconnected (although that could possibly be avoided by moving the disconnection code into a separate function). I've updated the question with Camera.h and Camera.cpp too!
Mikael
@Mikael: Looking at your `Camera` class you may be right. It might be easier to use pointers or smart pointers to manage your `Camera` instances than to make them copy-safe. You should declare a copy constructor and a copy-assignment operator for `Camera` and make them private for safety. If you have access to boost or tr1 you could use a `std::vector< tr1::shared_ptr<Camera> >` . If you use raw pointers you probably need to make your `CameraManager` non-copyable and add a simple loop deleting the dynamically allocated cameras in the desctructor. This would be a less robust option, though.
Charles Bailey
+1  A: 

You're storing raw pointers in your Camera class, but not defining a copy constructor or assignment operator (both of which std::vector requires for the above to work correctly). So, you get compiler-generated versions that do a shallow copy.

As a result of cameras.push_back(*camera1);, you're actually creating a temporary that is destroyed after the push_back returns. This temporary's destructor will release the resources you allocated in the statement Camera *camera1 = new Camera(NodeInfo,1, -44,0,0);, and the instance in the vector will have pointers to resources that are no longer valid.

You need to either not store the Camera by value in the vector (use a shared pointer of some sort) and explicitly mark the type as noncopyable/assignable, or you should provide the necessary copy/assignment semantics.

Also, not to mention you're leaking both camera1 and camera2.

Nathan Ernst