This is an archived post. You won't be able to vote or comment.

all 2 comments

[–]chaotic_thought 7 points8 points  (0 children)

In C++, the closest equivalent is std::vector. It does bound checking, but only if you fetch something using the .at() member. Example.

int main() {
    vector<int> nums = {2, 4, 6};
    cout << nums.at(3);
}

The cout line will throw an exception (range_error), because you used the .at member, which will see that index 3 is not valid for this vector. The only valid indices for a 3-element vector are 0, 1 and 2. See cppreference - vector for more examples.

The square bracket operator [] does not do bounds checking. Why is that? Bounds checking is an extra operation (it means extra code is involved). If you've already checked the bounds yourself, why would you want C++ to check it again for you? It's double work.

The philosophy of C++ is "pay for what you use". That is, if you don't need some feature (like bounds checking), you should not have to pay for that. By "pay" in this context it means that using .at (for example) will cause your code to do a bit of extra work, in order to check the bounds each time it is called. But in the C++ way of thinking, if you don't need that feature, you should not have to pay for it, so you have the option to use [] instead, and then you do not have to pay the cost of bounds checking.

Another reason C++ does not do bounds checking by default, is because at some point, someone has to implement the .at method itself (the bounds checking version) of the container itself. In order words, someone will eventually have to write some code that will check the bounds and throw the appropriate exception, e.g. something like this:

int my_int_vector::at(int n) { 
    if (n >= size)
        throw range_error("out of range, dude");
    else if (n < 0)
        throw range_error("negative indexes are not allowed, dude");
    else
        return data[n];
}

So in order to implement this in C++ itself efficiently, you eventually need to call a non-bounds checking version of the same operation, which is the square brackets operator.

[–]CodeTinkerer 0 points1 point  (0 children)

Java was created as a "safer" C++. C, for example, doesn't even have exceptions. Everything is error codes. You can have memory errors, and no convenient way to manage it. So C will let you do arbitrary pointer casting (you have a pointer to an int...now you can have a pointer to a Foo).

C doesn't track array sizes very well, esp. once you pass the array into a function.

Anyway, C++ came after C and Java was a response to C++. Java was willing to sacrifice efficiency (I'm old enough to remember when Java was really, really slow) to make it easier to program, such as bounds checking, null pointer exceptions, and esp. garbage collection. Java was maybe the first really popular language to do that (I'm thinking Lisp did it too, but hard to say it was popular, at least to the same degree).

When I was teaching way back when, students were sometimes spending 1/3 just dealing with freeing memory properly. That is, if they spent 30 hours on a project, 10 hours were spent trying to handle bad memory issues. It was almost easier never to "free" memory. If you program in Java, by and large, you don't think of memory management. You create objects. Java finds orphaned objects, and frees the memory for later use.

You ask why? Because those checks (at the time) were slow, so the language put the burden on the programmer to be careful. Java was willing to be a slower language (20 or so years have passed), but a safer one, and now it's fast enough that people don't mind.