-3

I am trying to build a lib that interfaces with Python, however Im running into so problems with VS2019. I have found a simple example that shows my problem:

#include <vector>
#include <iostream>

class Foo
{
public:
    std::vector<double> bar;

    void fillBar(Foo& foo)
    {
        int nrElements = 100;
        for (int i = 0; i < nrElements; ++i)
        {
            bar.emplace_back(0.0);
        }
    }
};

int main()
{
    Foo* foo = new Foo();

    std::cout << "Filling bar" << std::endl;
    foo->fillBar(*foo);

    std::cout << "deleting bar" << std::endl;
    delete foo->bar.data();

    std::cout << "The End" << std::endl;
}

This works fine, however if I try to increase nrElements to say 1.000.000 the line delete foo->bar.data() crashes the program. I guess it has something to do with the resizing of the std::vector when the current space is running out. But I would imagine that the data() would still return a pointer I can delete. Especially since when I dereference the pointer it gives the right value for all elements in the vector...

Frank
  • 2,446
  • 7
  • 33
  • 67
  • Voting to close as a typo. `delete foo->bar.data();` should be `delete foo;` as you need to delete the pointer you newed, not what the pointer points to. – NathanOliver Sep 11 '19 at 14:50
  • You actually don't need `new`, and so no need of `delete`. – Jarod42 Sep 11 '19 at 14:52
  • 6
    "I would imagine that the data() would still return a pointer I can delete" -- no, it doesn't do that, ever. A `vector` manages its own memory. What gave you the idea that messing around with it is something you should be doing? – molbdnilo Sep 11 '19 at 14:52
  • It's not a typo. I agree that the example is a bit derived, but why would this not work? I have this construction because of the interfacing with python (not specified here) – Frank Sep 11 '19 at 14:52
  • What is your motivation for trying to `delete` memory you don't own (i.e. which someone else will `delete` later)? I just don't understand what the idea here is? What are you hoping to achieve? – Max Langhof Sep 11 '19 at 14:53
  • 5
    @Frank You can only `delete` something that you `new`ed. You didn't `new` what `bar.data()` returns. You cannot `delete` it. If you want to release the memory, ask `bar` to do it for you with `clear()`. Its responsible for its own memory, you are not. – François Andrieux Sep 11 '19 at 14:53
  • Related : [Why isn't accessing a deleted object crashing my program?](https://stackoverflow.com/questions/14862297/why-isnt-accessing-a-deleted-object-crashing-my-program). It's not because you observe that the values are still there that they actually are. You have undefined behavior, which means anything can happen. The behavior you observed may stop or change at any time for any reason and cannot be relied upon. You've noticed that already where if the vector is large enough, the behavior suddenly changes for no reason. – François Andrieux Sep 11 '19 at 14:55
  • 2
    "why would this not work" - there's so many reasons I don't even know where to start. 1. vector's internal storage might not have been allocated with `new` but with e.g. `malloc`. 2. You are using `delete`, but you don't know if `vector` used `new` or `new[]` (or see 1.) 3. `data()` might not point to it's internal storage. 4. Even if none of the above occurs, `vector` will delete any memory it allocated during destruction (although this doesn't happen in your code, because you are leaking `foo`). **This memory is not yours to manage and you are not allowed to manage it**. – Yksisarvinen Sep 11 '19 at 15:12

1 Answers1

1

But I would imagine that the data() would still return a pointer I can delete

It's not a typo. I agree that the example is a bit derived, but why would this not work?

No you simply are not allowed to, because "8.5.2.5 Delete [expr.delete]" in the C++ standard (draft N4713 used) clearly states:

In a single-object delete expression, the value of the operand of delete may be a null pointer value, a pointer to a non-array object created by a previous new-expression, or a pointer to a subobject (6.6.2) representing a base class of such an object (Clause 13). If not, the behavior is undefined.

And undefined behaviour, or commonly short UB, is defined in "3.27 [defns.undefined]" as

Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

Meaning: You are not allowed to delete the pointer given by std::vector::data(). If you still do so and you do by delete foo->bar.data(), your program is non deterministic and therefore we are talking about use cases outside of rational, common sense, computer programming. Its allowed to work, but also allowed not to work, and any try to reason about it, is beyond the scope of whats commonly regarded as productive computer programming.

Of course you are allowed to delete foo; since foo is indeed a pointer to a non-array object created by a previous new-expression but foo->data() is not. It might be internally be some std::vector implementations, but that is neither guaranteed nor usually the case nor allowed in the cases it is, since every std::vector implementation would have to delete their internal memory at least upon destruction, causing a double delete, which also invokes UB. But again, most std::vectors not even use new, in the sense for allocating and constructing something.

Superlokkus
  • 4,731
  • 1
  • 25
  • 57