Calling std::vector constructor when containing class manually allocated

82 views Asked by At

I'm afraid to ask questions in case it turns out to be stupid... But I tried to search and don't see the same situation.

I'm retrofitting a std::vector into some existing legacy code that is mostly C style. Our next major release which isn't due for a year or two will jettison a lot of the legacy code. But for now, the way we work is, every project gets recompiled for the customer, depending on specs. Some projects are on Visual Studio 2008, some 2010, etc. My added std::vector code I'm working on has no visible problems when compiled with 2013, but, I get crashes within the STL code when running VS 2008 SP1.

The existing code has a struct, and a fixed size array in it:

#define MAX_REMOTE_CONN 75

typedef struct {
    int     rno;
    int     adrs;
    bool    integ_pending;
} RTUref;


typedef struct {
    char        device[64];
    int         port;
    RTUref  RTU[MAX_REMOTE_CONN];
    // more stuff...
} Connect_Info;

So, my basic goal is to get rid of the hard coded size limit to the RTU array. So, I have revised it like this:

class{
public:
    int     rno;
    int     adrs;
    bool    integ_pending;
} RTUref;

typedef std::vector <RTUref> RTUlist;

typedef struct {
    char        device[64];
    int         port;
    RTUlist     RTU;
    // more stuff...
} Connect_Info;

The Connect_Info structs are allocated using our own memory manager. Don't know much about it other than it is supposed to be more efficient than use malloc() and free(). I'm guessing that the constructor for RTU doesn't get called since the struct it is contained in data allocated by our own memory manager?

Nevertheless, the code where I size the array, put values into the array all at least seem to work okay. But, when I call .clear() I get a crash from within the STL. And as I said, only if I use 2008. If I use 2013, I don't get that crash.

Assuming pct is a pointer to an allocated Connect_Info structure, the the line:

pct->RTU.clear();

Generates a crash on VS 2008. I am able to resize and add elements to the array. And I even tried to add a check that I don't clear unless the size is greater than zero like so:

if (pct->RTU.size() > 0)
    pct->RTU.clear();

And I still get the crash on the clear.

So, I made the educated guess that I need to call a constructor. But, I wasn't quite sure of how to do it. But, in the code where the Connect_Info struct is allocated, I tried to add contructor code like this:

pct->RTU = RTUlist();

It compiles. But, I then get a crash in the STL on that line.

I haven't yet tried to build a small contained test program, as I'm not even sure that I will be able to reproduce the problem without our memory manager. But, I will try if that is what I need to do. I thought maybe someone might see obviously what I'm doing wrong. I'm fairly novice to the STL.

1

There are 1 answers

2
iAdjunct On

A little background: there is a term in C++ called "POD Type" (or "Plain Old Data Type").

There are verbose rules, but basically things that may do special things on allocations, deallocations, or copies are not POD types. The original Connect_Info was a POD type since it didn't do special things at those times and didn't have any non-POD members.

However, since you added a std::vector (which is not a POD type because it has to do special things at allocation, deallocation, copy, etc (because it allocates memory)), Connect_Info is not a POD type.

POD types can be allocated safely with malloc and deallocated with free since they don't do special things. However, non-POD types cannot (except in exceedingly rare cases which you'll first see after several years of programming C++) be allocated like that.

C only has POD types, so malloc is perfectly acceptable. There are a few options you can do:

int main ( ... )
{
    Connect_Info * info = new Connect_Info() ;
    std::cout << info->port << std::endl ;
    delete info ;
}

Or

Connect_Info * makeOne ()
{
    void * ptr = malloc ( sizeof(Connect_Info) ) ;
    if ( ! ptr ) return 0 ;
    return new (ptr) Connect_Info () ; // "In-Place constructor"
}
void deleteOne ( Connect_Info * info )
{
    if ( ! ptr ) return ;
    info = info->~Connect_Info() ; // manually call its destructor with the weirdest syntax ever
    // Note: I'm not 100% sure this call to 'free' is right because the in-place new can return a different pointer, but I don't know how to the get the original back
    free ( static_cast<void*>(info) ) ;
}
int main ( ... )
{
    Connect_Info * info = makeOne ()
    std::cout << info->port << std::endl ;
    deleteOne ( info ) ;
}

If you have boost available (or C++11, which you probably don't), this is a MUCH better option (and only uses header components of boost):

boost::shared_ptr<Connect_Info> makeOne ()
{
    return boost::make_shared<Connect_Info> () ;
}
int main ( ... )
{
    boost::shared_ptr<Connect_Info> info = makeOne ()
    std::cout << info->port << std::endl ;
    // nothing else: shared_ptr takes care of that for you
}

(If you have C++11, use std::shared_ptr and std::make_shared)