The Destructor, and declaring . . . returning an object of same type to calling object

I hope this won't be too vague but I don't want to be verbose. . . . I don't always seem to have this problem. I have this class, Text, whose objects consist of objects of another class, StringVar (they can be interpreted quite literally to infer what they are). Both have destructors. One of the member functions of Text is to copy a portion of the calling object and return a different Text object that represents the portion. Since Text's operator<< needs to do work, all I know to do is to declare a new Text object in this member function, adequately fill the object with StringVar objects and then return it. It works when there's no destructor for the class and doesn't work with the destructor. I don't remember having this problem with an analogous member function in StringVar when I was working on the textbook problem involving that class as the only user-defined class in the program (and in fact I was surprised at the time that the destructor apparently didn't have any effect on the returned object declared within the member function).

I'm guessing this would be a somewhat typical situation, so I spare you any code.
Last edited on
So my inquiry is about the gap in my understanding as far as what I've described.
I would guess it has to do with something like you are returning a temporary and so it's already been destroyed by the time you print it.

But it's really hard to tell without seeing any code. Can you post the code? Destructors and the problematic << operator at the very least.
Yes, sure. Suppose I have these test statements for StringVar:

1
2
3
char a1[] = "a-one";
StringVar obU(a1);
cout << obU.copyPiece(1, 3) << endl;

They work just fine, getting the output

-on

This is the relevant part of the StringVar class including definitions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class StringVar {
    public:
        StringVar(const char a[]);
        ~StringVar();
        StringVar copyPiece(const int& posVar, const int& lenVar) const;
        friend std::ostream& operator <<(std::ostream& outs, const StringVar& var);
    private:
        char *value;
        int maxLength; };

StringVar::StringVar(const char a[]) {
    maxLength = strlen(a);
    value = new char[maxLength + 1];
    strcpy(value, a); }

StringVar::~StringVar() {
    delete [] value; }

StringVar StringVar::copyPiece(const int& posVar, const int& lenVar) const {
    if(posVar < maxLength && lenVar <= maxLength - posVar) {
        StringVar obX(lenVar);
        int index(0);
        for(int i = posVar; i < posVar + lenVar; i++) {
            obX.value[index++] = value[i]; }
        return(obX); }
    else {
        StringVar obX(0);
        return(obX); } }

std::ostream& operator <<(std::ostream& outs, const StringVar& var) {
    outs << var.value;
    return(outs); }

Then suppose I have these test statements for Text:

1
2
3
char a2[] = "zero one two three four";
Text obV(a2);
cout << obV.copyPiece(1, 4) << endl;

They also work just fine, but only without the destructor. With the destructor commented out, the output is

one two three four

Otherwise, I get an "Unhandled exception at msvcr100d.dll . . . " message. This is the relevant part of the Text class including definitions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
class Text {
    public:
        Text(const int& sizeVar);
        Text(const char a[]);
        ~Text();
        friend std::ostream& operator <<(std::ostream& outs, const Text& var);
        Text copyPiece(const int& posVar, const int& lenVar) const;
    private:
        StringVar *value;
        int maxLength; };

Text::Text(const int& sizeVar) {
    if(sizeVar < 0) { maxLength = 0; }
    else { maxLength = sizeVar; }
    value = new StringVar[maxLength + 1];
    char a[] = " ";
    StringVar obX(a);
    value[maxLength] = obX; }

Text::Text(const char a[]) {
    maxLength = 1;
    int lastPos(0), indexA(0), indexB;
    bool lPosSet(false);
    for(int i = 0; i < strlen(a); i++) {
        if(lPosSet == false && lastPos == 0 && a[i] != ' ' && a[i] != '\t') {
            lastPos = i;
            lPosSet = true; }
        if(i > 0 && (a[i] == ' ' || a[i] == '\t') && (a[i - 1] != ' ' && a[i - 1] != '\t')) {
            maxLength++; } }
    if(a[strlen(a) - 1] == ' ' || a[strlen(a) - 1] == '\t') { maxLength--; }
    value = new StringVar[maxLength + 1];
    char *b;
    for(int i = 0; i <= strlen(a); i++) {
        if(i == strlen(a) || (i > 0 && (a[i] == ' ' || a[i] == '\t') &&
                             (a[i - 1] != ' ' && a[i - 1] != '\t'))) {
            b = new char[i - lastPos + 1];
            indexB = 0;
            for(int j = lastPos; j < i; j++) {
                b[indexB++] = a[j]; }
            b[indexB] = '\0';
            StringVar obX(b);
            value[indexA++] = obX;
            if(i < strlen(a)) { lastPos = i + 1; }
            else {
                char c[] = " ";
                StringVar obY(c);
                value[maxLength] = obY; }
            while(a[lastPos] == ' ' || a[lastPos] == '\t') {
                ++lastPos; } } }
    delete [] b; }

Text::~Text() {
    delete [] value; }

std::ostream& operator <<(std::ostream& outs, const Text& var) {
    for(int i = 0; i < var.maxLength; i++) {
        outs << var.value[i];
        if(i < var.maxLength - 1) { outs << " "; } }
    return(outs); }

Text Text::copyPiece(const int& posVar, const int& lenVar) const {
    if(posVar < maxLength && lenVar <= maxLength - posVar) {
        Text obX(lenVar);
        int idx(0);
        for(int i = posVar; i < posVar + lenVar; i++) {
            obX.value[idx++] = value[i]; }
        return(obX); }
    else {
        Text obX(0);
        return(obX); } }
Last edited on
*destructor
OK, you are copying objects, but you don't have a proper copy constructor.

Consider the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
    StringVar a("whatever");  // allocates memory for a.value
    {
        StringVar b = a;  // copies a to b
         // however!
         //  this doesn't actually copy the string data, it only copies the pointer
         //  now, b.value == a.value.  They both point to the same data
    } // here, b is destroyed
    //  this means that b's destructor will delete[] value.
    //  however note that 'a' is still using that buffer!  This is bad
} // here, a is destroyed
// and it tries to delete[] value.  But value was already deleted by b's destructor
//  so your program explodes. 


This is the source of your problem. You need to write copy constructors and assignment operators for these classes that actually "deep copy" the string data and not just copy the pointers.
Aha. I have a copy constructor and an assignment operator for the StringVar class but had neither for the Text class (even though the textbook problem suggested that I basically use all of the members of the StringVar class analogously for the Text class). That's why it worked for the StringVar class with the destructor, as I then took it too much for granted not really learning the lesson. Now I remember reading that the copy constructor and the =operator are part of "The Big Three" along with the destructor, where if one is needed all are needed.

So I know you're right even before I've written the code. I just needed you to hit me with it, with an added bonus that you gave me another good variant of the explanation. Thanks a lot, Disch.
Topic archived. No new replies allowed.