4

I thought I did something stupid - at least I thought it was. My question is: why does this work?

template<typename T>
class yarray
{
public:
    yarray() {
        pContainer = new T[1]; //initialize as array with size 1
        unCount = 0U; //counter
    }
    ~yarray() {
        delete[] pContainer; //cleanup
        pContainer = nullptr;
    }

    void push_back(T data)
    {
        ((T*)pContainer)[unCount] = data; //set (shouldn't it throw an exception when unCount > 0?
        unCount++; //increment
    }

    T operator[](const unsigned int & index)
    {
        if (index <= unCount)
            return ((T*)pContainer)[index];

        return T();
    }

private:
    void * pContainer;
    unsigned int unCount;
};

int main()
{
    yarray<int> klo;

    klo.push_back(342);
    klo.push_back(5563);

    for (auto i = 0; i < 2; ++i)
        std::cout << klo[i] << std::endl;
}

The code works perfectly in C++14 (Visual Studio). Shouldn't it throw an exception after the second push_back?

Update question: What if you don't initialize pContainer with new, but with pContainer = &T() instead? Doesn't that affect somehow the memory or even endanger other programs? When I use a class that prints out when it was constructed/destructed, all objects that were created will be destroyed right after the construction. Why can I use them even after destruction?

6
  • 3
    That's undefined behavior. It could seem to work, or it could crash, anything is possible. Commented Dec 20, 2015 at 2:30
  • 2
    delete[] pContainer is also undefined behaviour - it should be delete[] (T *)pContainer; Commented Dec 20, 2015 at 2:34
  • @M.M can you please elaborate more Commented Dec 20, 2015 at 2:37
  • @M.M, well, as I see here, all of the overloads to operator delete[] take void* anyway, so there would be no point of casting your pointer to an actual type pointer, since it would degrade back into void* anyway. Commented Dec 20, 2015 at 2:44
  • 1
    @M.M's point is that the code allocated a T*, so must delete a T*. This code deletes a void*. The compiler doesn't just call operator delete(). It calls destructors for the T object(s) that were allocated. Deleting a void* at best doesn't run any destructors; worse, it might corrupt memory. For scalar types that often doesn't matter, but it's still wrong. Commented Dec 20, 2015 at 2:48

2 Answers 2

7

A program with an out of bounds array access has undefined behavior. It is not required to throw an exception.

As to why it works, it's just bad luck.

Sign up to request clarification or add additional context in comments.

5 Comments

Thank you all for the fast answers. Would this be even safe?
I don't know what "safe" means these days. The behavior is undefined.
@terrakuh C++ is everything but not safe. Although, it can be used safely if you obey some rules. Managed languages achieve safety at the cost of performance loss. In this case the cost is to do runtime bound-check everywhere.
Thanks for all the answers. It helped
@hege_hegedus: Bounds-checking is not a question of the platform. std::vector::at performs bounds-checking in C++ (probably not what you would refer to as "managed").
1

The addressable memory of a computer is set to a number of pages. The current crop have 4k (4096 bytes). On a modern operating system, then 4k of memory is either addressable, or not addressable.

For the program to fail, then the memory would need to be

  1. buffed up to the end of a page xxxxxxxxxxxxDDDDD (x unimportant D data).
  2. Also the next page needs to be unmapped - not part of the process.

Only when these are true, does the system fault.

Programs such as valgrind (linux) and application verifier (windows) modify the memory allocation system to engineer this form of failure, but without this system, then the code would not always fault.

On simpler systems (such as arduino), then all memory is available, and this would never fault. What would happen, is that the memory would belong to something else, and cause a difficult to find fault.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.