Some errors in MIT's intro C++ course

  • Thread starter Alf P. Steinbach /Usenet
  • Start date

S

Squeamizh

    [...]
So, you have to teach:
To make an array you write:         std::vector<element_type>  v(size);
To access to the vector you write:  v.at(index)
Why the at?  That's an advanced feature, for the special (and
rare) cases where you want an exception on a bounds error,
instead of a crash.

LOL!  

If you prefer a crash, why not use raw arrays and have out of bounds
crashes all the time?

You seem to believe that the only advantage to using a std::vector
rather than a raw array is the at() member function.
 
Ad

Advertisements

J

Juha Nieminen

In comp.lang.c++ Leigh Johnston said:
Juha Nieminen said:
Since when has the safer variant been the "advanced feature", while the
variant which causes UB when misused is the one suitable for beginners?

The variant which cause UB when misused is the preferred choice because the
goal is to write bug-free software which doesn't contain any misuse rather
than to write software containing bugs which need to be protected against.
operator[] will typically have an assert which can be detected in *debug*
mode. Answer: test and test again. :)

operator[] is not guaranteed to catch out-of-bounds accesses in any
mode, while at() is.
 
J

Juha Nieminen

In comp.lang.c++ Christian Hackl said:
Juha Nieminen ha scritto:


For beginners it won't really make a difference.

It makes a huge difference if the program tells the user "hey, you
accessed this vector out of boundaries, bad, bad" rather than behaving
erratically for unknown reasons.
In practice, in both
cases their program will safely crash

In which system does accessing a vector out of boundaries "safely crash"
for sure?

In most systems I know of it *might* crash, but it might also corrupt
some unrelated object/variable, causing erratic behavior which can be
really hard to debug.

Using at() avoids that.
 
J

Juha Nieminen

Pascal J. Bourguignon said:
You can still do OO programming in assembler.

Not really. Unless you consider the "every variable is public" paradigm
a valid form OO programming.
 
K

Kai-Uwe Bux

Pascal said:
That's why you have to use a garbage collector.



Which is no problem for the garbage collector.


IMO, it would be better to teach assembler than C, up to the point of
having written a simple but complete program in assembler. Then
forget it completely for the rest of the cursus (apart of course in
the compiler course).



Still my point. If you have a language that you cannot use, but have
to use libraries built upon to make it usable, what's the point? Just
use a higher level language!

One can _use_ C++. The above, however, are remarks about _teaching_ C++: a
context, you snipped away.
You can still do OO programming in assembler.

No, I can't: the machines for which I once learned machine code are long
gone, and I did not keep up.
Why don't we?

a) Assembler is a fast moving target.
b) Assembler is, from what I see from a distance, less convenient.
c) I don't do OO anyway :)
Same here.

I doubt it.


Best

Kai-Uwe Bux
 
K

Kai-Uwe Bux

Joshua said:
I think they're more basic than std::vector in terms of "first
principles". std::vector is implemented in terms of dynamic memory
allocation and deallocation, not the other way around. However, this
is rather pedantic, and doesn't really capture any of our main points.
See the rest of my reply.


Perhaps. See below.


As long as we agree they should know how std::vector is implemented
eventually, I suppose it's a question of empirical fact as to which
order of learning is best.

Very, very much agreed.
As a guess purely on personal preference, I would prefer and I think I
would learn better if I learned how the internals of std::vector work
before hearing the explanation of the interface, contracts, and
guarantees of std::vector.

Also, if I was teaching an intro course for those who would take more
courses, I'm not sure I would focus as heavily on RAII and exception
safety as you do. Correctness of programs is important, but it's not
obvious to me that teaching someone with the use of fully correct
programs from the beginning is the best way to end up with someone who
writes fully correct programs at the end. As a (poor) analogy, I would
teach a student Newtonian physics before I taught them General
Relativity.

The point is not to teach RAII early on. The point is to use the library and
teach safe idioms early so that reasoning becomes simpler. In effect, I
would introduce RAII probably in the context of pointers: new and delete
have to be matched in pairs (that mantra, I would strongly reinforce when
teaching pointers); the language guarantees that constructors and
destructors of a given object are paired along all paths of flow control.
So, let's new in the constructor and delete in the destructor and let the
guarantees of C++ take care of that part of reasoning.

However, as I would teach pointers somewhat late in the process, RAII would
also come somewhat late.
Indeed. For a sane program, "data = new T;" may succeed, but the
assignment "*data = t;" may throw, causing the constructor to not
finish, causing the lifetime of the object to never begin, causing the
standard invocation of destructor to never occur, causing the "data =
new T;" to never be freed.

However, I remain unconvinced that explaining this in an introduction
to C++ course is the best way to start teaching a new C++ programmer.

There might be a slight misunderstanding: I would not even try to explain
that to a beginner. But when diving into pointers, I would feel the need to
explain something like that (maybe just in order to prevent bad coding
habits from being established). Hence, I would not explain pointers to
beginners and start elsewhere.

Here is a rough order that comes to mind:

* basic types: int, double, std::string.
* flow control.
* buildings complex types: classes and containers.
* standard library: iterators, algorithms, streams.
* function objects.
* exceptions and flow control via throw/catch.
* templates.
* smart pointers (e.g., tr1::shared_ptr<>, tr1::function<> )
* raw pointers and arrays.
* implementing all those nifty library features.

The elegance of pointers is that they can serve as a building block for so
many other things. But that power comes at the price of some huge (albeit
hidden) complexity.


Best

Kai-Uwe Bux
 
Ad

Advertisements

P

Pascal J. Bourguignon

Squeamizh said:
James Kanze said:
On Sep 10, 3:56 pm, (e-mail address removed) (Pascal J. Bourguignon)
wrote:
James Kanze ha scritto:
    [...]
So, you have to teach:
To make an array you write:         std::vector<element_type>  v(size);
To access to the vector you write:  v.at(index)
Why the at?  That's an advanced feature, for the special (and
rare) cases where you want an exception on a bounds error,
instead of a crash.

LOL!  

If you prefer a crash, why not use raw arrays and have out of bounds
crashes all the time?

You seem to believe that the only advantage to using a std::vector
rather than a raw array is the at() member function.

Yes, to be informed when there's an out of bound condition. I want to
know when there's a bug in my programs.
 
B

Balog Pal

Leigh Johnston said:
Since when has the safer variant been the "advanced feature", while the
variant which causes UB when misused is the one suitable for beginners?

The variant which cause UB when misused is the preferred choice because
the goal is to write bug-free software which doesn't contain any misuse
rather than to write software containing bugs which need to be protected
against. operator[] will typically have an assert which can be detected
in *debug* mode. Answer: test and test again. :)

That is a generalization of course as some domains are more critical than
others and require a higher degree of defensiveness.

Yeah. And throwing an exception -- and continue execution in a faulting
state that was actually discovered -- is hardly a wise choice. Or a
defendable one. Unless certainly the program was written with *intent* of
out-of-bounds access (there are tricks to use endless loop, and let
exception terminate it), but in C++ those ways are normally on the DON'T
list.

Read Matthew Wilson's fine article in the recent Overload issue.
 
P

Pascal J. Bourguignon

Les Cargill said:
So why the "should"? Simply because it's harder on the student?

No, because it's less productive for the enterprises. Even if very
good programmers can be as productive as they always are with any
programming language, for the average programmers and most
applications, C++ is not a good choice. It's not for nothing that
Java took so much market share.
 
P

Pascal J. Bourguignon

Les Cargill said:
Sorry, I don't buy that. First, the measurement of
computer programmer productivity simply hasn't been done.
KLOC per fortnight? Bleh :) How... how do you do that? If
someone is deliberate enough to create no negative externalities
in the organization *at all* but doesn't meet the method of
measure in the organization, (s)he will not be considered effective -
even though an economist may be able to prove (s)he actually
*is*.

It has been done. Granted, perhaps on smaller projects. But still.
And C++ doesn't come out to any advantage.

http://web.cecs.pdx.edu/~apt/cs457_2005/hudak-jones.pdf
http://wwwipd.ira.uka.de/~prechelt/Biblio/jccpprtTR.pdf
http://www.codinghorror.com/blog/2005/08/are-all-programming-languages-the-same.html

Second, when people have tried to do it, like in Fred Brooks'
book, the subject quickly turns to culture, not technology,
as the source of pain in software development.

Granted, there are other factors. That's not a reason to ignore the
programming language factor.

Third, the subject "this is how arrays work in raw 'C'" is
probably a good thing for computer science students to know.
it's elegant.

Interesting to a geek, I won't deny it. But in the grand scheme of
things, it's irrelevant. Once you switch to automobiles with
combustion engine, whether your horse show has 8 holes or 9 holes
doesn't matter much.

Then again, I got stuck with a product which used a version of
std::map that just *failed* several years back, and I simply
ripped the failed thing out and put in something simple - in
'C' - because I could actually more-or-less-partial-prove correctness
of the thing in a test stand independent of the system - that worked.

Yes, that's a general problem with big libraries.
 
J

Juha Nieminen

In comp.lang.c++ Christian Hackl said:
Juha Nieminen ha scritto:
operator[] is not guaranteed to catch out-of-bounds accesses in any
mode, while at() is.

operator[] is guaranteed to catch out-of-bounds accesses in the MSVC
documentation of std::vector, when compiled with appropriate settings.
So if I settled on VC for my (imaginary :)) course, then my students
would get crashes.

I don't think teaching a specific compiler (and making the students rely
on features of that compiler) in a generic C++ course is the proper thing
to do.

You could *mention* that in VC, when compiling in debug mode, operator[]
will have boundary checks, but you should make it clear that this might not
be the case with all compilers, as it's not demanded by the standard. Then
you could say that the at() method is guaranteed to have boundary checks.
 
Ad

Advertisements

J

Juha Nieminen

In comp.lang.c++ Christian Hackl said:
Juha Nieminen ha scritto:


I'm afraid I don't understand. How could it do anything like this if
there's an assertion leading to abort()?

Could you show me the part of the C++ standard which guarantees that
std::vector::eek:perator[] will abort() when it's accessed out of boundaries?
Of course, if I was teaching this to students, I'd make sure that the
target platform/compiler of the course guaranteed a crash.

I think you would be teaching them to rely on a feature which is not
guaranteed by the standard.
 
K

kwikius

* Alf P. Steinbach /Usenet, on 08.09.2010 21:15:


6.096 Introduction to C++
As taught in: January IAP 2009
Level:
Undergraduate / Graduate
Course Description
This course is designed for undergraduate and graduate students in science,
social science and engineering programs who need to learn fundamental
programming skills quickly but not in great depth.

[snip list of errors]

I'm happy to report that the MIT course ...

[http://ocw.mit.edu/courses/electrical-engineering-and-computer-scienc...

6.088 Introduction to C Memory Management and C++ Object-Oriented Programming
As taught in: January IAP 2010

Level:
Undergraduate
]

... appears to have far fewer errors.
[...]

- Alf (newfound calling, to evaluate MIT C++ courses :) )

Hope you sent them a consulation fee + expenses + third home +
travel to exotic locations for essential conferences + contribution
towards essential 10 year sabbatical due to stress of paid 3 month
summer holiday breaks etc...


regards
Andy Little
 
K

kwikius

Christian Hackl said:
James Kanze ha scritto:
[...]

So, you have to teach:
To make an array you write: std::vector<element_type> v(size);
To access to the vector you write: v.at(index)

Why the at? That's an advanced feature, for the special (and
rare) cases where you want an exception on a bounds error,
instead of a crash.

#include <vector>

int main)
{
std::vector<int> v(100U, 0);
v[100] = v[-1];
}

compiled without complaint ... didnt crash .. Great! Program must be
working .. ;-)

regards
Andy Little
 
J

Johannes Schaub (litb)

Alf said:
* Paul N, on 10.09.2010 13:34:

Hi Paul.

First, it's not a matter of personal opinion, as it seems you think.

C++ has an international standard, ISO/IEC 14882, that defines these
things.

Essentially (I'm not sure how much of "virtual" you know about), when a
class Derived /overrides/ a method m from class Base, and you have a
pointer p of type Base* that points to an object originally created as
Derived, then writing p->m() invokes D::m.

That does not happen for a non-virtual method. When m is non-virtual the
expression p->m() invokes Base::m, because the known static type is Base.
In this case a/the redefinition of m in Derived is not an override.

Officially, it's called hiding. These two have different meanings.
Redefinition occurs for class names. In particular, for typedef names that
are declared in a scope where there is also a class declared. The class name
is redefined (instead if being hidden) by the typedef declaration to be then
a typedef name that refers to that class.

But if you have a base class and a derived class, the derived class defines
its own function, distinct from the base class function. Calling this
"redefinition" could cause confusion, I think. It does to me, at least.
 
A

Alf P. Steinbach /Usenet

* Johannes Schaub (litb), on 11.09.2010 17:53:
Officially, it's called hiding.

Yes, I mentioned that in the very next paragraph (not quoted).

These two have different meanings.
Redefinition occurs for class names. In particular, for typedef names that
are declared in a scope where there is also a class declared. The class name
is redefined (instead if being hidden) by the typedef declaration to be then
a typedef name that refers to that class.

No, "redefinition" is just an informal descriptive term. It means what it says,
no more or less. E.g. in the standard it's also used about redefining a
freestanding function, and in Stroustrup's TCPPPL 2nd ed. the index entry for
redefinition points to a discussion of redefinition of an enumeration value.

But if you have a base class and a derived class, the derived class defines
its own function, distinct from the base class function. Calling this
"redefinition" could cause confusion, I think. It does to me, at least.

Hm. Well, it's a definition, and it defines a name to stand for something else
than it would have stood for without that definition, hence, redefinition. :)


Cheers,

- Alf
 
Ad

Advertisements

J

James Kanze

On Sep 11, 6:06 am, (e-mail address removed) (Pascal J. Bourguignon)
wrote:

[...]
It has been done. Granted, perhaps on smaller projects.
But still. And C++ doesn't come out to any advantage.

Very small projects. Small enough that scripting languages
have a definite edge. (Up to about 1000 LOC, I tend to use
AWK, rather than C++.) And none of the studies mentioned
study the maintainability of the results.

If the project is such that you can reasonable implement in
AWK (to choose a scripting language I know), then the AWK
implementation will probably be easier than one in C++. If
the project is large enough to require many separate
translation units (classes, etc.), then C++ or (I suspect)
Ada95 will come out better than Java (although Java does
work well for smaller projects, just above the size for
which typical scripting languages work).
 
J

James Kanze

In which system does accessing a vector out of boundaries
"safely crash" for sure?

Windows, when using VC++. Linux (and all of the other
Unices), when using g++.
In most systems I know of it *might* crash, but it might
also corrupt some unrelated object/variable, causing
erratic behavior which can be really hard to debug.

You don't know Windows? Or g++? You're in a rather
exceptional situation, then. (OK, until about 2 years ago,
I'd never used either Windows or g++ professionally. But I
still knew they existed, and knew a little bit about them.)
Using at() avoids that.

By causing an exception, which will cause more problems than
immediately crashing.
 
J

James Kanze

On Sep 10, 3:56 pm, (e-mail address removed) (Pascal J. Bourguignon)
wrote:
James Kanze ha scritto:
[...]
So, you have to teach:
To make an array you write: std::vector<element_type> v(size);
To access to the vector you write: v.at(index)
Why the at? That's an advanced feature, for the special
(and rare) cases where you want an exception on a bounds
error, instead of a crash.
#include <vector>
int main)
{
std::vector<int> v(100U, 0);
v[100] = v[-1];
}
compiled without complaint ... didnt crash .. Great!
Program must be working .. ;-)

It crashes with all of the compilers I use.
 
Ad

Advertisements

J

Johannes Schaub (litb)

Alf said:
[Re-posted to comp.lang.c++ because Johannes had redirected follow-ups to
comp.lang.c++, thus removing my reply from comp.programming. Johannes:
DON'T DO THAT, and please don't snip a statement and replace with your own
saying the same, as if it hadn't been said. Those are not-nice discussion
tactics.]

I did not mean to say that you did not notice that it's called hiding. I
just wanted to express my discomfort with that term, no more and no less :)

I'm sorry about the follow-ups thing, didn't notice my client does such a
thing by default. Hope you can forgive me once again? I'm all eager to fall
into nice discussions with you.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top