Some errors in MIT's intro C++ course

  • Thread starter Alf P. Steinbach /Usenet
  • Start date
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.]

* 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.

Looks like you are right. It says also "Unless redefined in the derived
class, members of a base class are also considered to be members of the
derived class." but I personally dislike this usage. "redefined" to
"redeclared" should have the same relationship as "defined" to "declared"
has. But in this case, it does not seem to be the case, because "redeclared"
has a very specific non-collegial meaning.
 
A

Alf P. Steinbach /Usenet

* Johannes Schaub (litb), on 11.09.2010 22:21:
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.

He he. I can't remember this happening earlier. So.

Cheers,

- Alf
 
P

Pascal J. Bourguignon

Christian Hackl said:
Juha Nieminen ha scritto:
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.

I would not teach a specific compiler.

But when you organise a programming course, you also need practical
exercises, and in order to do that correctly it is imperative that you
establish a target platform and compiler which is available to all
students throughout the semester and on which on all homework will be
tested.

Besides, no C++ compiler can (or should) be used sanely without custom
settings. It would be great if students could just type "g++ main.cpp"
or "cl main.cpp", but you have to tell them that they must also set
flags like -Wall or /Za before even being able to explain them their
exact meaning. By enforcing certain compiler options you will also
prohibit proprietary extensions.

I find portability very valuable. If I were teacher, I would run
homeworks on as many different compilers and plateforms I could
(including 36-bit hardware, and EBCDIC systems), and I would multiply
the grade by the ratio of successful runs.
 
J

Jorgen Grahn

["Followup-To:" header set to comp.lang.c++.]
Windows, when using VC++. Linux (and all of the other
Unices), when using g++.

What did you do to the real James Kanze?

salix:/tmp% cat foo.cc
#include <vector>
#include <iostream>

int main()
{
int bar[] = { 3, 3, 1, 8, 2 };
std::vector<int> foo(bar, bar+5);
for(unsigned i=0; i<10; ++i) {
std::cout << foo << '\n';
}
return 0;
}
salix:/tmp% g++ -W -Wextra -pedantic -std=c++98 -o foo foo.cc
salix:/tmp% ./foo
3
3
1
8
2
0
135137
0
0
0
salix:/tmp%

Possibly I'm missing some context here, e.g. if you were talking about
special debug builds.

/Jorgen
 
J

Jorgen Grahn

["Followup-To:" header set to comp.lang.c++.]

On Sat, 2010-09-11, Christian Hackl wrote:

....
But that's what I said right from the beginning: I'd tell them that
formally you cannot count on operator[] detecting an error, but in the
context of the course and with a quality implementation used with
correct compiler settings it will work as expected,

I don't see how that helps the students. If they want such detection,
they can run their programs under valgrind or Purify (which they
should be taught to do anyway, to catch a dozen other common newbie
errors).
and it is also the
commonly preferred form in "real" code, not only in exercises at
university.

I have never, ever used a bounds-checking std::vector<T>::eek:perator[].
And I have never seen others use it, either. I rely on it to be as
fast as C array indexing.

I have heard rumors that such checks are enabled by default in the
Microsoft world ... but then I have also heard people complaining
"ooh, the standard containers are too slow for me, I must use raw
C-style arrays!" from the same world.

/Jorgen
 
J

Joshua Maurice

On Fri, 2010-09-10, Joshua Maurice wrote:

...


Unlike programs written in some other language ... ?

Yes. For example Java. If a library has a programming error in Java,
it is not heinous to throw an exception like it is in C++. Due to the
security guarantees of Java, a bug in the Java code can't corrupt
memory or do anything else particularly bad to the process, unlike a
bug in C++.
 
S

SG

Windows, when using VC++.  Linux (and all of the other
Unices), when using g++.

What did you do to the real James Kanze?

    salix:/tmp% cat foo.cc
    #include <vector>
    #include <iostream>

    int main()
    {
        int bar[] = { 3, 3, 1, 8, 2 };
        std::vector<int> foo(bar, bar+5);
        for(unsigned i=0; i<10; ++i) {
            std::cout << foo << '\n';
        }
        return 0;
    }
    salix:/tmp% g++ -W -Wextra -pedantic -std=c++98 -o foo foo.cc
    salix:/tmp% ./foo                                  
    3
    3
    1
    8
    2
    0
    135137
    0
    0
    0
    salix:/tmp%

Possibly I'm missing some context here, e.g. if you were talking about
special debug builds.


Maybe.

Although students should know what guarantees the C++ standard makes
and what is compiler-specific, I think they should also be familiar
with the compiler's documentation they are working with. This includes
a possible debug mode the implementation may offer.

http://gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode.html

Cheers!
SG
 
I

Ian Collins

Yes. For example Java. If a library has a programming error in Java,
it is not heinous to throw an exception like it is in C++.

It isn't heinous to throw an exception in C++, it is perfectly acceptable.
Due to the
security guarantees of Java, a bug in the Java code can't corrupt
memory or do anything else particularly bad to the process, unlike a
bug in C++.

So throwing an exception and terminating isn't bad for the process?
 
Ö

Öö Tiib

["Followup-To:" header set to comp.lang.c++.]

Windows, when using VC++.  Linux (and all of the other
Unices), when using g++.

What did you do to the real James Kanze?

[...]
Possibly I'm missing some context here, e.g. if you were talking about
special debug builds.

The production builds are actually the "special" ones. Developers work
most of the time with tests, then debug builds then production builds.
Even the testers work more with debug builds. NDEBUG is best to keep
undefined in production builds as well (at least when compiling non-
performance critical parts). Then most asserts are checked on field
too and crash.

In similar way if it is not some performance critical container and
the implementation lets to mix the debug and release code then you can
use mostly debug containers even in production builds. On cases of
problems with performance about 9 times of 10 it is fault of wasteful
algorithms used and not bounds-checked containers.

Currently compilers can produce buffer overrun checking code even for
raw C buffers, and i think it is good to teach as first thing to
students how to turn every sort of checks on. It is wrong to turn such
things off before everything is tested.
 
J

Joshua Maurice

It isn't heinous to throw an exception in C++, it is perfectly acceptable..

That's a difference of opinion. In C++, if a sanity check on a class
invariant is violated (which can only happen from a programming error,
or a cosmic ray, etc.), then my default action is to kill the process,
leaving a core dump or equivalent. If a sanity check is violated in
Java, then I /may/ throw an AssertionError, and let the caller decide
what to do.
So throwing an exception and terminating isn't bad for the process?

This is the oft repeated argument I spoke about earlier. Let me copy
and paste my earlier arguments.

http://groups.google.com/group/comp.lang.c++/msg/d18ffde8d09d4f4d

Hopefully my only addition to this oft repeated argument is that real
robust applications tend to be robust from "firewalling" or separating
the program into isolated units. In C++ on unix like systems, this is
commonly at the process level. For crazy embedded systems, I've heard
about mission critical applications being written by 3 independent
teams using 3 different algorithms, and there was a small election
holder system isolated from the 3, and it would act on the majority
vote. Any dissenting voter was reset.

C++ programs by their very nature are incredibly brittle and fragile.
One wrong line anywhere can cause the entire process to do very weird
things. To get robustness, isolate and have redundancies, backups,
transactions, failovers and automatic resets, etc.

You're welcome to disagree. Many people do. Preferably we won't rehash
the same arguments over and over again though.
 
I

Ian Collins

That's a difference of opinion. In C++, if a sanity check on a class
invariant is violated (which can only happen from a programming error,
or a cosmic ray, etc.), then my default action is to kill the process,
leaving a core dump or equivalent. If a sanity check is violated in
Java, then I /may/ throw an AssertionError, and let the caller decide
what to do.

That is a design decision, not something imposed by the programming
language.
 
Ö

Öö Tiib

That is a design decision, not something imposed by the programming
language.

I would prefer to be treated with medical equipment that Joshua
Maurice has developed using C++. The one that he develops using java
might continue killing me despite it just moment ago realized that it
is insane.
 
J

Joshua Maurice

I would prefer to be treated with medical equipment that Joshua
Maurice has developed using C++. The one that he develops using java
might continue killing me despite it just moment ago realized that it
is insane.

I do not think that that is an accurate reflection of what I said. I
specifically stated that good fault tolerance in programs (of all
kinds and languages) is the result of fault isolation. In C++, this
can only really be accomplished at the process level because of the
way C++ is. However, Java has stricter security guarantees on
misbehaving programs, so you can achieve fault isolation inside of a
process in Java to a reasonable level. I would still use fault
isolation in the Java medical equipment just like the C++ medical
equipment, but the fault isolation may not be at the process level in
Java.
 
J

Joshua Maurice

That is a design decision, not something imposed by the programming
language.

Yes. I agree. I hope that you're not trying to use that to dispute
anything I have said. Are you? In which case I am lost and humbly
request clarification please.
 
I

Ian Collins

Yes. I agree. I hope that you're not trying to use that to dispute
anything I have said. Are you? In which case I am lost and humbly
request clarification please.

I am doing my best to avoid getting drawn into yet another pointless
language dick swinging contest.

Sure a C++ program can operate at a lower level than a Java one, but not
all (and probably very little) harm is done at the low level. A
misbehaving Java program can do just as much damage to it's data or
peers as a misbehaving C++ program can.

Most harm is done through poor design, not poor execution.
 
J

Joshua Maurice

I am doing my best to avoid getting drawn into yet another pointless
language dick swinging contest.

I am trying to avoid that as well. That is not my intent.
Sure a C++ program can operate at a lower level than a Java one, but not
all (and probably very little) harm is done at the low level.  A
misbehaving Java program can do just as much damage to it's data or
peers as a misbehaving C++ program can.

Most harm is done through poor design, not poor execution.

Ok. I guess we disagree on facts. I believe that a misbehaving Java
library cannot mess up the process as easily as a misbehaving C++
library. The C++ library could trash the entire memory subsystem with
a single bad line of code, which under certain coding styles is quite
easy to make. A race condition with the status quo or with the
upcoming standard. However, short of maliciousness, Java doesn't have
these problems. It's still possible that a bug in a library borks your
whole program, but the odds seem to be a lot less. It can't
inadvertently trash the memory subsystem, or cause a seg fault from a
race condition, etc.

Of course, it's all a matter of degree. Process boundaries are only
good to a degree. Separate physical hardware gives better fault
isolation than separate processes under Linux. In the end, as you say,
good design is required. After that, it all depends. Perhaps your
particulars require dumping core when your Java process hits a
programmer bug. I'm not in a position to comment on your design goals,
how good your fault isolation in the Java process is, etc. For
example, a misbehaving Java library could still make calls into other
libraries, which might trash the program. It's a judgment call as to
the proper response.

I just wanted to make the observations:
1- Fault tolerance requires fault isolation.
2- Generally one cannot reliably isolate faults inside of a C++
process. Fault isolation must be at the process level for C++.

After some replies, I made one final claim:
3- It's much easier to get more reliable fault isolation inside of a
single process in other languages, like Java, as opposed to C++.

And I definitely don't mean to get into a language dick waving
contest. I am just noting that fault isolation does not necessarily
need to be at the process level. it depends on the particulars.
 
I

Ian Collins

I am trying to avoid that as well. That is not my intent.


Ok. I guess we disagree on facts. I believe that a misbehaving Java
library cannot mess up the process as easily as a misbehaving C++
library. The C++ library could trash the entire memory subsystem with
a single bad line of code, which under certain coding styles is quite
easy to make. A race condition with the status quo or with the
upcoming standard. However, short of maliciousness, Java doesn't have
these problems. It's still possible that a bug in a library borks your
whole program, but the odds seem to be a lot less. It can't
inadvertently trash the memory subsystem, or cause a seg fault from a
race condition, etc.

I agree up to a point and I'd agree fully if we were comparing Java with
C. But C++ has mechanisms to enable objects to manage the access to and
lifetime of any dynamic memory they use. This levels the playing field
between C++ and Java applications. I's go further and say the ability
to manage any resource type with RAII tips the balance on favour of C++.
Of course, it's all a matter of degree. Process boundaries are only
good to a degree. Separate physical hardware gives better fault
isolation than separate processes under Linux.

Why just under Linux?
In the end, as you say,
good design is required. After that, it all depends. Perhaps your
particulars require dumping core when your Java process hits a
programmer bug. I'm not in a position to comment on your design goals,
how good your fault isolation in the Java process is, etc. For
example, a misbehaving Java library could still make calls into other
libraries, which might trash the program. It's a judgment call as to
the proper response.

I seldom have cause to use Java, most of what I write is either close to
the metal, or close to the OS.
I just wanted to make the observations:
1- Fault tolerance requires fault isolation.
Agreed.

2- Generally one cannot reliably isolate faults inside of a C++
process. Fault isolation must be at the process level for C++.

Disagree. In my option C++ is no worse than Java in this regard. I
will concede that the developer has to take greater care in C++, but
where fault isolation isn't an issue, not having to care can have its
advantages!
After some replies, I made one final claim:
3- It's much easier to get more reliable fault isolation inside of a
single process in other languages, like Java, as opposed to C++.

I'd dispute "much". I'd also note that this greater protection from the
language has a cost: the reduction of the utility of the language.

One of the beauties of C++, its multi-paradigm nature means you can
choose to write code in the style of a number of other languages. I
have C++ code that's almost C (device drivers), code that could be
mistaken for Java (my XML libraries implement the Java DOM bindings) and
code that could be mistaken for PHP (my web application libraries). If
an application demands the fault isolation of Java, it can be just as
easily be written in a Java like subset of C++ as it can in Java.
 
J

Joshua Maurice

I agree up to a point and I'd agree fully if we were comparing Java with
C.  But C++ has mechanisms to enable objects to manage the access to and
lifetime of any dynamic memory they use.  This levels the playing field
between C++ and Java applications.  I's go further and say the ability
to manage any resource type with RAII tips the balance on favour of C++.


Why just under Linux?

Sorry. I didn't want to say just "process" in that context as I
thought it was overly vague, so I picked a random operating system
which has good fault isolation between processes.
Disagree.  In my option C++ is no worse than Java in this regard.  I
will concede that the developer has to take greater care in C++, but
where fault isolation isn't an issue, not having to care can have its
advantages!

To be clear, I am not making any claims about Java at this point. I am
merely making the claim, to have a robust C++ product on a modern
desktop-like OS, you need to have the fault isolation at the process
level. No comparisons to Java. It's just a simple fact of making
robust C++ applications.
I'd dispute "much".  I'd also note that this greater protection from the
language has a cost: the reduction of the utility of the language.

One of the beauties of C++, its multi-paradigm nature means you can
choose to write code in the style of a number of other languages.  I
have C++ code that's almost C (device drivers), code that could be
mistaken for Java (my XML libraries implement the Java DOM bindings) and
code that could be mistaken for PHP (my web application libraries).  If
an application demands the fault isolation of Java, it can be just as
easily be written in a Java like subset of C++ as it can in Java.

I think it's not quote over empirical fact now. I don't think I'll be
able to convince you, but I don't think you fully get where I'm coming
from, so bear with me.

The question central in this discussion is what to do in the face of a
programming error when detected by a sanity check in the program. In a
"safer" language like Java, it is difficult and unlikely for a
programming error to break the memory subsystem, scramble random bits
of random objects, etc. In fact, Java has many guarantees on this
subject. However, yes, my argument rests on the assumption that the
JVM is correct, which isn't necessarily true, but it seems like a
reasonable enough assertion when compared to most desktop C++ programs
which have their correctness conditional on their operating system,
their "virtual" machine.

You just argued that with care, you can achieve the same benefits of
Java in C++. In some sense, I agree fully. However, my very premise, a
programmer error, contradicts "with great care by the programmer".
We're examining what should we do in the very real and common
occasions where the programmer "was not careful enough". In this case,
Java offers a lot more guarantees about process stability than C++
(conditioned on the correctness of the JVM).

When designing for robustness, one must anticipate that programmers
will not always be careful enough. Designing for robustness is
designing with the expectations of programmer bugs (and more). One
must have fault tolerance. One does this with redundancies, backups,
rollovers, transactions, etc., and most importantly fault isolation.
In this regard, C++ is very different than Java. (Not "better", as I
don't want to go there, just different.)

The very nature of Java, lack of pointer arithmetic, defined and
"sensible" outcomes from race conditions, defined and "sensible"
behavior on null pointer accesses, etc., aka all of its security
features, gives Java a higher degree of fault isolation intra-process
than C++.

Fault isolation by definition is how isolated are separate parts of
your application from faults in other parts of your application. In
Java, it's basically impossible to corrupt the bits of a local stack
object of someone's else's thread which did not have any escaped
references, but it's trivial for that to happen in C++ from a
programmer mistake.
 
K

kwikius

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.

my compiler it seems just uses unchecked offset from dynamically
allocated pointer... Its a freebie though from Gnu org or so... hmm..
maybe its just a load of rubbish.. Maybe I should have bought the VC+
+6 instead?

regards
Andy Little
 

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

Members online

Forum statistics

Threads
473,769
Messages
2,569,577
Members
45,052
Latest member
LucyCarper

Latest Threads

Top