• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 394
  • Last Modified:

ATL/COM: How to create/maintain an array of CComObjects?

Hi,

I am changing the implementation of our own grid control form using an array of structs for all cells to using an array of Com objects. I have a working solution, but I don't seem to be able to delete the objects I created, which shows in practice as a huge resource/memory leak.
I would like them to be in an array, but if there's a better/easier solution I will certainly look into it (but not MFC!). Furthermore, in the current implementation I create every object anew, but I guess copying/cloning to be much faster. It's probably easy to implement, I just don't know how.

This is how my declaration looks:
CComObject<CFastGridCell>*     m_lpCellsProp;

This is how it is updated when the dimensions of the grid change:
m_lpCellsProp = new CComObject<CFastGridCell>[m_cols * m_rows + 1];
for(int irow=1; irow<=m_rows; irow++)
{
    for(int icol=1; icol<=m_cols; icol++)
    {
        int id = getCellID(irow, icol);
        m_lpCellsProp[id] = new CComObject<CFastGridCell>();
        //do some things on the object, like:
        m_lpCellsProp[id].put_Default(props);
    }
}

I have no idea how the delete statement should like. I didn't get any to work. Maybe I do it all wrong. I never worked with arrays of some template type, let alone arrays of CComObject types. If someone knows how to do the same with multidimensional arrays, it'll be even more appreciated.

Btw, the above implementation works well in debug builds, but gives an access denied error after the first tens of object's constructs/destructs.

Btw2, the idea is to provide some kind of aggregation, but again, I never did that, except for simple objects like IFont and such.

Thanks in advance for any ideas,
12Bsure corp.
0
12bsure
Asked:
12bsure
  • 10
  • 6
1 Solution
 
AxterCommented:
//Example delete
for(int irow=1; irow<=m_rows; irow++)
{
   for(int icol=1; icol<=m_cols; icol++)
   {
       int id = getCellID(irow, icol);
       delete m_lpCellsProp[id];
   }
}

0
 
AxterCommented:
I think your code should look like the following for initialization:
m_lpCellsProp = new CComObject<CFastGridCell>[m_cols * m_rows + 1];
for(int irow=0; irow<=m_rows; irow++)
{
   for(int icol=0; icol<=m_cols; icol++)
   {
       int id = cols*m_rows + irow;
       m_lpCellsProp[id] = new CComObject<CFastGridCell>();
       //do some things on the object, like:
       m_lpCellsProp[id].put_Default(props);
   }
}

//Example delete
for(int irow=0; irow<=m_rows; irow++)
{
   for(int icol=0; icol<=m_cols; icol++)
   {
       int id = cols*m_rows + irow;
       delete m_lpCellsProp[id];
   }
}

0
 
12bsureAuthor Commented:
Which means that I cannot simply use a call similar to?:
delete [] somevar;

But is the space allocated with the following statement deallocated properly?
m_lpCellsProp = new CComObject<CFastGridCell>[m_cols * m_rows + 1];
0
Visualize your virtual and backup environments

Create well-organized and polished visualizations of your virtual and backup environments when planning VMware vSphere, Microsoft Hyper-V or Veeam deployments. It helps you to gain better visibility and valuable business insights.

 
AxterCommented:
I missed the greater or equal sign.  I took them out on the following code:

m_lpCellsProp = new CComObject<CFastGridCell>[m_cols*m_rows];
for(int irow=0; irow<m_rows; irow++)
{
  for(int icol=0; icol<m_cols; icol++)
  {
      int id = cols*m_rows + irow;
      m_lpCellsProp[id] = new CComObject<CFastGridCell>();
      //do some things on the object, like:
      m_lpCellsProp[id].put_Default(props);
  }
}

//Example delete
for(int irow=0; irow<m_rows; irow++)
{
  for(int icol=0; icol<m_cols; icol++)
  {
      int id = cols*m_rows + irow;
      delete m_lpCellsProp[id];
  }
}

0
 
AxterCommented:
Now that I come to think of it, that code is wrong too.

You have to do an individual delete, and then do a delete for the pointers.

Give me a minute, and I'll see if I can post a correct version.
0
 
12bsureAuthor Commented:
> int id = cols*m_rows + irow;
Basically that is what I did in getCellID:
long CFastGrid::getCellID(long Row, long Col)
{
     if(Row > m_rows || Col > m_cols || Row < 1 || Col < 1)
          return (0);

     long iRet = (Row - 1) * m_cols + Col;
     if(iRet > m_rows * m_cols)
          return (0);
     else
          return(iRet);
}


Do you have any idea how to copy the CComObject? Because this way it's dreadfully slow!
0
 
AxterCommented:
Where are you copying it now?
0
 
AxterCommented:
Where in your code are you copying CComObject?
0
 
12bsureAuthor Commented:
Ah, now I see it. Sorry, I didn't look close enough. You create a zero-based array and I start (deliberately) on one. The reason is, as you can see from getCellID, that I want a dummy-cell to be returned to the client when the client requests the wrong cell. I also use it in the implementation when I don't know for sure I get a cell (like on mouseover). It eases my programming a bit.
0
 
12bsureAuthor Commented:
> Where in your code are you copying CComObject?
I don't. I haven't got a clue on how to do it. IFont has a Clone method, maybe I need to implement my own, but I hope there's an easier way.
0
 
AxterCommented:
This is an example using foo and foofoo as the classes:

class foofoo
{
public:
     int x;
};

template<class T>
class foo
{
public:
     foo(){};
     T data;
};

int main(int argc, char* argv[])
{
     int m_cols = 32;
     int m_rows = 16;
     foo<foofoo>* m_lpCellsProp = new foo<foofoo>[m_cols * m_rows];
     for(int irow=0; irow<m_rows; irow++)
     {
          for(int icol=0; icol<m_cols; icol++)
          {
               int id = icol*m_rows + irow;              
               //do some things on the object, like:
               m_lpCellsProp[id].data.x = 3;
          }
     }
     return 0;
}
0
 
AxterCommented:
I forgot the delete:

class foofoo
{
public:
     int x;
};

template<class T>
class foo
{
public:
     foo(){};
     T data;
};

int main(int argc, char* argv[])
{
     int m_cols = 32;
     int m_rows = 16;
     foo<foofoo>* m_lpCellsProp = new foo<foofoo>[m_cols * m_rows];
     for(int irow=0; irow<m_rows; irow++)
     {
          for(int icol=0; icol<m_cols; icol++)
          {
               int id = icol*m_rows + irow;              
               //do some things on the object, like:
               m_lpCellsProp[id].data.x = 3;
          }
     }
     delete [] m_lpCellsProp;
     return 0;
}


0
 
AxterCommented:
You only have to use New once, and delete once.
0
 
12bsureAuthor Commented:
I'm glad it's all that simple after all. I've had so many different tries that it never occurred to me that I could leave the tedious new-statements in the loop. A few hours ago I was hopelessly lost in the intricacies of ATL/COM, but now everything looks simple and easy again! Thanks a lot!

FYI, this is the current implementation. I still have a small resource leak, but not that huge anymore as I had before. It's probably in another part of my code.

void CFastGrid::setDimensions(long irows, long icols)
{

     if(((m_rows) && (m_cols))          //Only the first call these are zero;
          || (!irows && !icols))          //Only the last call these are zero
     {
          delete [] m_lpCellsProp;
          if(!irows && !icols)          //We are in the destructor, bail out
               return;
     }
     m_rows = irows;
     m_cols = icols;

     m_lpCellsProp = new CComObject<CFastGridCell>[m_cols * m_rows + 1];

     ATLASSERT(m_lpCellsProp);
     for(int irow=1; irow<=m_rows; irow++)
     {
          for(int icol=1; icol<=m_cols; icol++)
          {
               int id = getCellID(irow, icol);
               m_lpCellsProp[id].setDefaults(val1, val2 valX);
          }
     }

}

PS: The speed penalty from all the new-statements is completely gone. Now it's fast again!

PS2: It's good to know that the code looks like I learned my basics, instead of the clumsy workarounds and buggy code I ended up with.

Thanks alot for your help!
0
 
AxterCommented:
On the for() loop, shouldn't it start at zero?
With it starting at 1, that would mean that id, would never equal to zero, and since you have a zero based variable, that would mean that the first col and row, would never get filled.

I'm not sure if you account for that in your getCellID() function.

Everything else looks good.
0
 
12bsureAuthor Commented:
No. At least, I think I did it correctly. Maybe you missed on one of my comments (some went so fast). For convenience, I'll repeat it here:

1. 06/25/2001 06:03PM PST
> int id = cols*m_rows + irow;
Basically that is what I did in getCellID:
long CFastGrid::getCellID(long Row, long Col)
{
    if(Row > m_rows || Col > m_cols || Row < 1 || Col < 1)
         return (0);

    long iRet = (Row - 1) * m_cols + Col;
    if(iRet > m_rows * m_cols)
         return (0);
    else
         return(iRet);
}

2. 06/25/2001 06:07PM PST
Ah, now I see it. Sorry, I didn't look close enough. You create a zero-based array and I start (deliberately)
on one. The reason is, as you can see from getCellID, that I want a dummy-cell to be returned to the
client when the client requests the wrong cell. I also use it in the implementation when I don't know
for sure I get a cell (like on mouseover). It eases my programming a bit.




As you can see, I use the very first of the array as some kind of special purpose cell.

I'll get you the points shortly. Again: thanks a lot!
0

Featured Post

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 10
  • 6
Tackle projects and never again get stuck behind a technical roadblock.
Join Now