Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Observable behavior and undefined behavior -- What happens if I don't call a destructor?

Tags:

Note: I've seen similar questions, but none of the answers are precise enough, so I'm asking this myself.

This is a very nitpicky "language-lawyer" question; I'm looking for an authoritative answer.

The C++ standard says:

A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor. For an object of a class type with a non-trivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.

I simply do not understand what "depends on the side effects" means.

The general question is:

Is forgetting to call a destructor any different than forgetting to call an ordinary function with the same body?

A specific example to illustrate my point is:

Consider a program like this below. Also consider the obvious variations (e.g. what if I don't construct an object on top of another one but I still forget to call the destructor, what if I don't print the output to observe it, etc.):

#include <math.h> #include <stdio.h>  struct MakeRandom {     int *p;     MakeRandom(int *p) : p(p) { *p = rand(); }     ~MakeRandom() { *p ^= rand(); } };  int main() {     srand((unsigned) time(NULL));        // Set a random seed... not so important     // In C++11 we could use std::random_xyz instead, that's not the point      int x = 0;     MakeRandom *r = new MakeRandom(&x);  // Oops, forgot to call the destructor     new (r) MakeRandom(&x);              // Heck, I'll make another object on top     r->~MakeRandom();                    // I'll remember to destroy this one!     printf("%d", x);                     // ... so is this undefined behavior!?!     // If it's indeed UB: now what if I didn't print anything? } 

It seems ridiculous to me to say this exhibits "undefined behavior", because x is already random -- and therefore XORing it another random number cannot really make the program more "undefined" than before, can it?

Furthermore, at what point is it correct to say the program "depends" on the destructor? Does it do so if the value was random -- or in general, if there is no way for me to distinguish the destructor from running vs. not running? What if I never read the value? Basically:

Under which condition(s), if any, does this program exhibit Undefined Behavior?

Exactly which expression(s) or statement(s) cause this, and why?

like image 641
user541686 Avatar asked Jan 14 '14 09:01

user541686


1 Answers

I simply do not understand what "depends on the side effects" means.

It means that it depends on something the destructor is doing. In your example, modifying *p or not modifying it. You have that dependency in your code, as the output would differ if the dctor wouldn't get called.

In your current code, the number that is printed, might not be the same number that would have returned by the second rand() call. Your program invokes undefined behavior, but it's just that UB here has no ill effect.

If you wouldn't print the value (or otherwise read it), then there wouldn't be any dependency on the side effects of the dcor, and thus no UB.

So:

Is forgetting to call a destructor any different than forgetting to call an ordinary function with the same body?

Nope, it's not any different in this regard. If you depend on it being called, you must make sure it's called, otherwise your dependency is not satisfied.

Furthermore, at what point is it correct to say the program "depends" on the destructor? Does it do so if the value was random -- or in general, if there is no way for me to distinguish the destructor from running vs. not running?

Random or not doesn't matter, because the code depends on the variable being written to. Just because it's difficult to predict what the new value is doesn't mean there's no dependency.

What if I never read the value?

Then there's no UB, as the code has no dependency on the variable after it was written to.

Under which condition(s), if any, does this program exhibit Undefined Behavior?

There are no conditions. It's always UB.

Exactly which expression(s) or statement(s) cause this, and why?

The expression:

printf("%d", x); 

because it introduces the dependency on the affected variable.

like image 188
Nikos C. Avatar answered Sep 20 '22 07:09

Nikos C.