Click to See Complete Forum and Search --> : Getting confused by a simple program


pim42
May 5th, 2003, 02:32 PM
Ok so I think I'm having trouble with a class in my program so I created a small program to try to see what is going on. I am confused by the results. It seems that just when I think I understand C++ and OOP I find out I have been doing something wrong.
This is my BaseData class( in base.h):
class BaseData
{
public:
BaseData(){
static int ctor=0;
printf("ctor %d\n",ctor);
ctor++;

name = new char[30];
strcpy(name,"");
}

~BaseData(){
static int dtor=0;
printf("dtor %d\n",dtor);
dtor++;

delete[] name;
}

BaseData(const BaseData& p){
static int cctor=0;
printf("copy ctor %d\n",cctor);
cctor++;

name = new char[30];
*this = p;
}

BaseData& operator=(const BaseData& p){
static int assign=0;
printf("assign %d\n",assign);
assign++;

if(this != &p)
{
strcpy(name,p.name);
sensNum = p.sensNum;
}
return *this;
}

LPTSTR name;
BYTE sensNum;
};


This is my main:#include <windows.h>
#include <stdio.h>
#include "base.h"

int main()
{
BaseData b, t1;
BaseData* t2, t3;

strcpy(b.name,"Name");
b.sensNum = 42;
printf("b: %s %d\n",b.name,b.sensNum);

t1 = b;
printf("t1: %s %d\n",t1.name,t1.sensNum);

t2 = new BaseData(b);
printf("t2: %s %d\n",t2->name,t2->sensNum);
delete t2;
/*
t3 = new BaseData; //this statement won't compile
t3 = b;
printf("t3: %s %d\n",t3->name,t3->sensNum);
delete t3;
*/
return 0;
}

The output is:
ctor 0
ctor 1
ctor 2
b: Name 42
assign 0
t1: Name 42
copy ctor 0
assign 1
t2: Name 42
dtor 0
dtor 1
dtor 2
dtor 3

First off why is the dtor called 4 times? t3 is never created so why is it begin destroyed? Second, though I think this is a result of using printfs, why is ctor 2 printed before b: Name 42? Third and most important, the commented out portion doesn't work and I do not understand why. Can someone explain? Finally does this class leak memory and/or what is wrong with it if anything? (I just started having problems in my larger program and I think it has something to do with this class).

Philip Nicoletti
May 5th, 2003, 03:15 PM
The line in your code :


BaseData* t2, t3;


is the same as :


BaseData* t2;
BaseData t3; // not BaseData* t3;

pim42
May 5th, 2003, 03:22 PM
Oh ok... now that you say that I remember. I used to put the * on the variables and not the type and recently changed.

It is amazing how much you can forget when you don't use it for awhile. I now have:

BaseData b, t1, *t2, *t3;

Well that answers my first and second questions, still have the third.

pim42
May 5th, 2003, 03:32 PM
NEVERMIND, all I had to do now is change t3 =b to *t3=b.

you fixed all my problems and I feel stupid for having them. thanks for the help.

Paul McKenzie
May 5th, 2003, 03:53 PM
Originally posted by pim42
Finally does this class leak memoryAre you trying to wrap handling of char data in a class? If so, why not just use std::string?

#include <string>
class BaseData
{
private:
std::string name;

public:
BaseData(){
static int ctor=0;
printf("ctor %d\n",ctor);
ctor++;
}
};

Note that you really do not need a copy ctor, assignment operator, or destructor if you use std::string. I've cut the size of your class down to a fraction of what it was originally, does virtually the same thing as your original code does, and it is maintainable.

Regards,

Paul McKenzie

pim42
May 5th, 2003, 03:58 PM
no this was actually a dumbed down version of a hardware sensor class. I'm interfacing a C dll so I'm using LPTSTR to match the function calls. (You might recall from pervious posts that I am required to use the same data types as the dll interface, thus no std::string).

Paul McKenzie
May 5th, 2003, 04:42 PM
Originally posted by pim42
no this was actually a dumbed down version of a hardware sensor class. I'm interfacing a C dll so I'm using LPTSTR to match the function calls. (You might recall from pervious posts that I am required to use the same data types as the dll interface, thus no std::string). Use vector<TCHAR>, or if the DLL doesn't modify your buffer, std::string::c_str(). Same thing, less headaches.

For example, if your DLL function is prototyped like this:

void func(LPTSTR s);

The way you would call it using a vector is as follows:

vector<TCHAR> vt( 20 ); // or whatever the number of characters
//...
func(&vt[0]);

The std::vector is the gateway to interfacing to 'C' functions. The vector stores its data in contiguous memory.

To specify a char *, int *, long*, whatever *, the way to do it is to create a vector of the type desired, and just specify the address of the first element when calling the function. Therefore, your entire class interface can be simplified to this:

#include <string>
class BaseData
{
private:
std::vector<TCHAR> name;

public:
BaseData() : name(31, 0) {
static int ctor=0;
printf("ctor %d\n",ctor);
ctor++;
}
};

Again, no new, delete, copy constructor, op=, or destructor is necessary. This trick of using the std::vector instead of the "new[] / delete[]" stuff that a lot of code is littered with makes the program less problematic and easier to maintain. Also, if I want to resize the name field, all I have to do is call name.resize(). I don't have to do the "new / copy / delete " coding.

Scott Meyer's book, "Effective STL", has an entire chapter dedicated to interfacing to 'C' functions using the standard containers.

Regards,

Paul McKenzie