1

I have a question about triggering a destructor for an object prematurely. I have an dynamically allocated array of pointers to dynamically allocated Word objects. The name of the array is words_. words_ is a class attribute of another class called Dictionary.

In my Dictionary class, I have a function where I access each Word object and call a member function of the Word class.

The below code triggers the destructor prematurely:

Word *curr_word_ptr = words_[idx]; // This line is okay, doesn't trigger destructor
Word curr_word = *curr_word_ptr; // This line triggers the destructor prematurely
curr_word.callMemberFunc();

Because of the second line, as soon as the scope of the function ends, the destructor is called.

But if I access it through the array indices alone:

*(words_[idx]).callMemberFunc(); // The desctructor is not called prematurely

Is the problem because I have a stack variable (not even a stack pointer variable) accessing a dynamically allocated object? Thus, when the scope of the function ends, both the stack variable (curr_word) and the dynamically allocated stack object gets destroyed?

Thank you.

Stephen Wong
  • 177
  • 1
  • 8
  • Would you like to see the Word class? Or how the words_ array is dynamically allocated? Or the Dictionary function? – Stephen Wong Jul 27 '20 at 06:56
  • `Word curr_word` creates an instance of `Word` in with automatic storage duration, which is destructed when the scope in which `curr_word` is created ends. Why do you expect anything else? – t.niese Jul 27 '20 at 06:57
  • @PIprog3592 Yes, basically all relevatnt code, or even better a true [mre], unless the current answer solves your problem. I wasn't sure what destructor you meant, but it seems to be clear now? – Lukas-T Jul 27 '20 at 07:00
  • The c++ specification does not talk about `stack` and `heap` but about storage duration and lifetime. If the tutorial or book you use for learning only talks about stack and heap, and not in combination with storage duration, then you probably should consider choosing a different book/tutorial. – t.niese Jul 27 '20 at 07:01
  • @t.niese Just curious, what is incorrect about refering to stack and heap in c++? Does it not work that way? Or is it more complex than stack and heap? – Stephen Wong Jul 27 '20 at 07:06
  • @PIprog3592 stack and heap are a way how an implementation can follow the rules of the specification. But from the perspective of lifetime it is better to learn it the way how the specification defines the lifetime of objects. Stack and heap can - to a certain degree - be used to illustrate certain things, but it is just not how it is defined. – t.niese Jul 27 '20 at 07:21

2 Answers2

6

Word curr_word = *curr_word_ptr; defines a local Word, that only lives in the local scope, your function. When the function exits, it is destroyed. Note it is a copy of the initial Word that is destroyed, not the original.

If you want handy syntax so you don't have to dereference all over, or make a copy, use a reference:

Word &word = *curr_word_ptr;

This is the syntactic sugar C++ provides to refer to an object without pointers (directly). Note though, any changes made to word will affect your original word as well in this case.

The problem is even worse if your Words contains dynamically allocated memory. Assuming you use the default copy constructor, the addresses will be copied. As such, destruction of a local Word will free memory still referenced by the original Word, causing a crash when next accessed.

kabanus
  • 24,623
  • 6
  • 41
  • 74
  • So only the copy of the initial Word is destroyed? That's weird, because it called the destructor for the dynamically allocated Word object in the array itself, which crashed the program when another function tried to access the missing object in the array. – Stephen Wong Jul 27 '20 at 07:02
  • 3
    @PIprog3592 I doubt that. Likely, the word contains itself pointers references. As such, when copied, you make a shallow copy - so when the local word is destroyed, it frees (possibly) these pointers. The original is not destroyed, but will still reference free memory (and crash). Without a full example I can't know for sure, but this seems likely. – kabanus Jul 27 '20 at 07:03
  • @PIprog3592 If you want to ask a separate question on that, I think that would be fine (assuming you need a longer clarification than my comment). You would need to provide a fuller example. – kabanus Jul 27 '20 at 07:04
  • The Word class itself contained a dynamically allocated array of c strings. When I saw the destructor being called, it deleted the array of c strings for the Word object. Is that the culprit? – Stephen Wong Jul 27 '20 at 07:05
  • @PIprog3592 You've got it. The original is not destroyed, but the local destructor frees the array. Next time your original word accesses this - kaboom. – kabanus Jul 27 '20 at 07:06
  • @PIprog3592 then you most certainly did not follow the [rules of zero/three/five](https://en.cppreference.com/w/cpp/language/rule_of_three). – t.niese Jul 27 '20 at 07:06
  • t.niese is correct, google that - it's an important concept, especially when dealing with dynamic memory. – kabanus Jul 27 '20 at 07:06
  • @kabanus How is the original Word object not being destroyed, but yet the local destructor frees the array? – Stephen Wong Jul 27 '20 at 07:08
  • https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three?rq=1 is probably relevant – Alan Birtles Jul 27 '20 at 07:08
  • @PIprog3592 If two things point to cell 0x10 in memory, then when one frees it, the other thing still points there (you made no changes to that, a pointer is just a number). Please ask a separate question on this matter if it is unclear, this comment thread is too long. – kabanus Jul 27 '20 at 07:10
  • @PIprog3592 Because you shallow copied the array to the local object. As already said you are not following the rule of three. This code is not going to work until you understand that concept and correct your `Word` class. – john Jul 27 '20 at 07:10
  • @kabanus Last question. I understand how the local Word object triggers the destructor now. However, let's say I just stick to the first line of the code by using the pointer curr_word_ptr. If I do curr_word_ptr->word(), this works also. By using the pointer, we are accessing the Word object in the heap, and so we are not making a local copy of the object itself? Correct? – Stephen Wong Jul 27 '20 at 07:58
  • @PIprog3592 Correct. Using a reference as in my example also works (or a local **pointer**, that would be the same as a reference except you would use `->` everywhere instead of `.`). – kabanus Jul 27 '20 at 08:25
1
Word curr_word = *curr_word_ptr;

creates on stack copy of the object and that copy gets destroyed.

Most probably you class has missing a logic and you need define copy constructor to prevent crashes or disable copy constructor so you can create copy by mistake like here.

*(words_[idx]).callMemberFunc();

Invokes method directly on object pointed by object stored in array. Copy is not created.

Other way you can write this line:

words_[idx]->callMemberFunc();

I see also a problem since you are using raw pointers. Since c++11 this approach is considered bad practice. Learn to use std::uniqie_ptr and std::shared_ptr.

Also use std::vector instead regular C-array

Marek R
  • 32,568
  • 6
  • 55
  • 140