Sign in to follow this  
Kyall

Is C++ too complex?

Recommended Posts

Sudi    761
[quote name='Paradigm Shifter' timestamp='1354581397' post='5006860']
If only they could get rid of some of the mistakes they made earlier on and make a cleaner lighter language. Not going to happen though.
[/quote]

The language is light....i really don't see the problem alot of you seem to have with it. Its the same with any programming language. Once you know how to programm you can basicly programm in any language.

Share this post


Link to post
Share on other sites
Khatharr    8812
[quote name='Paradigm Shifter' timestamp='1354581397' post='5006860']
If only they could get rid of some of the mistakes they made earlier on and make a cleaner lighter language. Not going to happen though.
[/quote]

Wish I could upvote you twice. I don't know if I'd call them 'mistakes' but some of the changes seem a lot more like 'tack on another class' rather than actually incrementing the language itself to add new features. Edited by Khatharr

Share this post


Link to post
Share on other sites
Sudi    761
[quote name='Paradigm Shifter' timestamp='1354582898' post='5006876']
You've probably never seen any really bad C++ code. C is a very light language but my, have I seen some terrible code which no-one understands. C++ is about 4 times worse if you write it badly.
[/quote]
I think i have seen alot of code and c can just be as bad as c++. Same goes for every programming language. You can always write bad code and create structures that no one understands besides yourself. But that really has not so much todo with the language used.

Share this post


Link to post
Share on other sites
ApochPiQ    23011
Take the [url=http://www.scapecode.com/2011/05/a-simple-c-quiz/]infamous C++ quiz[/url] by Washu and tell me C++ is not problematically bad.

You can write bad code in any language, sure. You can also commit genocide with any weapon, but some weapons make it a hell of a lot easier.

Share this post


Link to post
Share on other sites
Cornstalks    7030
This one just got me the other day:

[quote]
Which constructors and/or operators will be called in this code:
[code]
struct MyStruct
{
MyStruct() {}
MyStruct(int i) {}
MyStruct(const MyStruct&) {}

MyStruct& operator = (int) { return *this; }
};

MyStruct s = 5;
[/code]
[/quote]
[spoiler]
This is an example of [url="http://stackoverflow.com/questions/1051379/is-there-a-difference-in-c-between-copy-initialization-and-direct-initializati"]copy-initialization[/url] ([url="http://www.gotw.ca/gotw/036.htm"]see a GotW about it[/url]). First, [font=courier new,courier,monospace]MyStruct(int)[/font] will be called to convert [font=courier new,courier,monospace]5[/font] to a [font=courier new,courier,monospace]MyStruct[/font]. Second, [font=courier new,courier,monospace]MyStruct(const MyStruct&)[/font] will be called with the created temporary. The other two functions ([font=courier new,courier,monospace]MyStruct()[/font] and [font=courier new,courier,monospace]operator =()[/font]) are not called. The code is effectively [font=courier new,courier,monospace]MyStruct s(MyStruct(5))[/font] (fingers crossed that isn't accidentally a function declaration...).

It gets a little more complicated though, because of [url="http://en.wikipedia.org/wiki/Copy_elision"]copy elision[/url], so the compiler is allowed to optimize away the temporary and effectively just call [font=courier new,courier,monospace]MyStruct(5)[/font]. But it doesn't have to.
[/spoiler]

C++ is way too complex. I don't mean too complex for beginners. I mean it's way more complex than it needs to be.

Beginners can start with whatever they want. They just need to realize that it'll probably take them longer to get something shiny on the screen with C++. Like SiCrane said, no matter what language someone chooses, they'll make plenty of mistakes. It's not the end of the world if someone chooses C++. But it's not exactly a walk in the park, either. It's never a walk in the park. Edited by Cornstalks

Share this post


Link to post
Share on other sites
Sudi    761
Ok when i saw that code i thought hmm pretty clear it should be the int constructor since the object doesn'exist yet....then i saw your explanation and i shrug...really? then i tried it just to be sure....first instinct was right your "spoiler" is wrong

Share this post


Link to post
Share on other sites
Grafalgar    548
Ok, reading through the comments, and the general "it's too complex, not a walk in the park, long time for something shiny on the screen, etc" hand-wavy consensus - I'm really curious: What, specifically, from the folks that consider the language overly complex, are the overly complex pieces of the language?

And I'm not talking about relatively obscure tricks/"abuses" of the languages. I'm talking about stuff you'll learn in your first years of Comp Sci, or an intro-to-C++ book.

I mean, I've seen my fair share of heinous code. In all manner of languages: C, C++, Java, C#, Python, LUA .. you name it. But what makes it complex has never been the language as much as the complexity of the code. In other words, I rarely ask myself: "Damn, what does this language feature do?" but rather ask "why in the world has the developer done this?"

I do believe, though, that pointers and the usage thereof continually trip folks up with regard to C *and* C++, and with good reason. Pointer management takes a while to get a good grasp of, but even Pascal had pointers and I can count on my left foot the number of times someone has said "Pascal is too complex" :) Even though Java and C# have *many* more language features than C and C++ combined, the fact that they (mostly) completely hide pointers and memory management makes it easier for most programmers to write code and not worry about memory consequences, right off the bat.

So, again, what *exactly* makes C++ so complex over, say, Java? Pointers aside, and considering only what you may learn in your first year as a CS student? (I make that distinction because as you spend more time in any language you'll eventually learn all sorts of 'interesting' things that'll make most folks' heads spin)

Share this post


Link to post
Share on other sites
ApochPiQ    23011
That question makes no sense to me.

The problem is that [b]no[/b] first-year CS course will cover all the things you have to know to use C++ proficiently. It's like asking "what's so hard about being a chef? Limit your answers to the subject of boiling water."

Well, yeah, boiling water isn't hard. There's a lot more to being a chef than boiling water.

Share this post


Link to post
Share on other sites
[quote name='Sudi' timestamp='1354587120' post='5006908']
Ok when i saw that code i thought hmm pretty clear it should be the int constructor since the object doesn'exist yet....then i saw your explanation and i shrug...really? then i tried it just to be sure....first instinct was right your "spoiler" is wrong
[/quote]This is a good demonstration of how confusing C++ is. Cornstalks told you exactly what happens, you went and tested it, and you are still wrong.

He said that the int constructor gets called, then the copy constructor gets called, but the compiler is allowed to optimize the copy constructor away. In the case of your particular compiler, your particular compiler version, your particular computer architecture, your particular compiler settings it so happens that the compiler does that optimization and only calls the int constructor. But with some other configuration of those things, the same code would result in both constructors being called, and in both cases the compiler could be a completely valid C++ compiler. This repeats over and over again in the language: valid C++ compilers are allowed to do a wide range of things, and if you want your code to be valid C++, it has to withstand all of those things. Therefore you have to know what the [i]language[/i] says. If you don't know the copy constructor may get called there and you assume it won't, that can blow up in your face at some point in the future.

Share this post


Link to post
Share on other sites
Cornstalks    7030
[quote name='Sudi' timestamp='1354587120' post='5006908']
Ok when i saw that code i thought hmm pretty clear it should be the int constructor since the object doesn'exist yet....then i saw your explanation and i shrug...really? then i tried it just to be sure....first instinct was right your "spoiler" is wrong
[/quote]
Did you miss the comment about [url="http://en.wikipedia.org/wiki/Copy_elision"]copy elision[/url]? The compiler is allowed to optimize away the temporary, [b]even if there are side effects[/b]. It doesn't have to, but it may (and most probably will). Some compilers may, and some may not. Some may with certain optimization settings, some may not. But it's important to remember that you may be constructing two [font=courier new,courier,monospace]MyStruct[/font]s (the temporary, and the one you want) because, depending on what [font=courier new,courier,monospace]MyStruct[/font] is and what its constructor/destructor does, there may be side effects. It's another one of these "Betcha didn't see that coming, now did ya?!" that C++ can throw at you, because you test it a thousand times on a compiler/platform, and it works a specific way, and then you test the same code on another compiler/platform and it does something a little different.

If you don't believe me that both the [font=courier new,courier,monospace]int[/font] constructor and copy constructor are used in that code, try this:
[code]
class MyClass
{
private:
MyClass(const MyClass&) {} // It shouldn't need this, right?

public:
MyClass() {}
MyClass(int i) {}
MyClass& operator = (int) { return *this; }
};

MyClass s = 5;
[/code]
[url="http://ideone.com/LJwyry"]As expected, compilation fails.[/url] So now we see that the copy constructor is indeed needed and used. But what happens if we run this code:
[code]
#include <iostream>

class MyClass
{
public:
MyClass() { std::cout << "MyClass()" << std::endl; }
MyClass(int) { std::cout << "MyClass(int)" << std::endl; }
MyClass(const MyClass&) { std::cout << "MyClass(const MyClass&)" << std::endl; }
MyClass& operator = (int) { std::cout << "MyClass& operator = (int)" << std::endl; return *this; }
};

int main()
{
MyClass s = 5;
}
[/code]

[url="http://ideone.com/f339ge"]It's a big WTF[/url] if you ask me. The copy constructor is required for it to compile (because it's technically required to do [font=courier new,courier,monospace]MyClass s(MyClass(5))[/font]), but it's never even called in the final code! Classic example of copy elision.

@Grafalgar: My posts show one example of exactly what you're looking for. Beginners are taught early on about constructors, but this little detail can potentially be a big pain in the butt that they don't learn until years later. Edited by Cornstalks

Share this post


Link to post
Share on other sites
Grafalgar    548
[quote name='ApochPiQ' timestamp='1354589878' post='5006930']
That question makes no sense to me.

The problem is that [b]no[/b] first-year CS course will cover all the things you have to know to use C++ proficiently. It's like asking "what's so hard about being a chef? Limit your answers to the subject of boiling water."

Well, yeah, boiling water isn't hard. There's a lot more to being a chef than boiling water.
[/quote]

OK, that's fair, but the point is that the stuff I deal with regularly with regards to C++ I, personally, do not find very hard nor complex. Instead of 1st year students, let's expand to what one might learn [i]throughout[/i] your 4-year degree. Or maybe in your first year being a software engineer. A lot of things that are considered "too complex" I very rarely encounter, and that's clear across 14 years of dedicated C++ development. Moreover, what I'm particularly interested in is what makes C++ more complex than Java (or C#), at the beginner/novice level, without going into language obscurities that [i]most[/i] people may not deal with.

Share this post


Link to post
Share on other sites
ApochPiQ    23011
[list][*]Dynamic memory management of any form
[*]Type coercion rules
[*]Various syntactical "quirks" (semicolon placement is one I hear a lot)
[*]Translation unit model and linking/symbol visibility/etc.
[*]Exception safety
[*]Const correctness
[*]Multiple inheritance
[*]How templates really work (critical if you want to use the standard library [i]at all[/i])
[*]Undefined behavior[/list]

That's just a small selection, too, based on your request for things that are virtually unavoidable in the language.

These are all things that I see bite just about every C++ newbie I've ever worked with, and routinely bite people all over this community. None of those problems exist in, say, C#.

Share this post


Link to post
Share on other sites
alvaro    21247
I've never heard any complaints about semicolons. The fact that you should put one after a struct or class block but not after a namespace block is kind of weird, though.

Multiple inheritance is avoidable (and should be avoided).

I would add making sure code is portable to your list. There are quite a few complexities to handle there (type sizes, byte order, alignment, padding, standard headers that may or may not include other standard headers...).

Share this post


Link to post
Share on other sites
CS_    236
My pet peeve: that whole header file / code file separation. Why does C(++) want me to juggle those two when every other language I've ever been exposed to conveniently uses one file for both? That's why I decided to make C# my primary language. I just try to design my projects around the supposed ~5% loss of overall speed.

Share this post


Link to post
Share on other sites
SiCrane    11839
[quote name='ApochPiQ' timestamp='1354752535' post='5007576']
Exception safety
[/quote]

Well, to be fair, exception safety can be a pain in any language. It's true that in C# you don't have to worry about leaking managed resources due to an exception, but it's still easy to stick your program in an inconsistent state especially if you don't follow DRY.

My problem with C++ as a language for beginners is that beginners normally learn by experimentation. They may read a book or take a class but at the end it's them with a compiler trying things out. The first consequence is that in this beginning stage it's very easy for them to think they've learned something about C++ that's actually incorrect. It's not like as beginners they have the experience to generate full coverage tests for their mini-experiments. Even if they do discover something correct about their own compiler they often mistake implementation specific behavior for behavior of the language as a whole. Heck, I've seen teachers do the same thing (evaluation order springs to mind).

The second consequence is that languages with clean grammars and good errors make for much better beginning languages because they can get logical immediate feedback. The semi-colon placement issue is usually a problem because the errors generated frequently point at the "wrong" line of code. Related to this is times when C++ will compile code into something very different than what was intended. The most vexing parse is one of these, but then you've got temporaries created when people wanted to actually wanted to call the base constructor in a derived constructor body, and similar.

Finally, C++ as a first language seems to discourage developing problem solving skills. Beginners learn that when they get stuck against a brick wall, it's probably the language they're fighting and rush off to get help with the latest syntax error or undefined behavior problem. And for people who learned a different language first, that's probably true 99% of the time; however, for someone who's trying to learn C++ at the same time as learning the basics of programming that may not be true in the majority of cases, but true often enough that running off to get help is positively reinforced.

There are people who have successfully learned to program with C++ as their first language. However, it seems like people who start with a different language and then learn C++ later generally get to the level of a proficient C++ programmer faster, and I've seen plenty of both.

[quote name='CS_' timestamp='1354758972' post='5007598']
My pet peeve: that whole header file / code file separation. Why does C(++) want me to juggle those two when every other language I've ever been exposed to conveniently uses one file for both?[/quote]
It's basically inherited from C, which was developed in a time when computers were sufficiently low on memory that they literally couldn't fit all the symbols necessary for a complex program in memory all at the same time and still have room for actual code. Programmers would be able to identify which symbols were really needed and include only those headers. Edited by SiCrane
quotes

Share this post


Link to post
Share on other sites
CS_    236
[quote name='SiCrane' timestamp='1354759002' post='5007599']
It's basically inherited from C, which was developed in a time when computers were sufficiently low on memory that they literally couldn't fit all the symbols necessary for a complex program in memory all at the same time and still have room for actual code. Programmers would be able to identify which symbols were really needed and include only those headers.[/quote]

Thank you, though I should have been more clear in indicating that I do understand the reason, but simply cannot stand the additional handling complexity. Of course it has its raison d'etre even today (embedded systems come to mind), but I might see the language in a more positive light if the .h/.c(pp) separation wasn't all but mandatory when targeting systems with plenty of resources. My apologies.

Share this post


Link to post
Share on other sites
[quote name='ApochPiQ' timestamp='1354584922' post='5006892']
Take the [url="http://www.scapecode.com/2011/05/a-simple-c-quiz/"]infamous C++ quiz[/url] by Washu and tell me C++ is not problematically bad.
[/quote]
Actually, decided to take a look to it, and I think the answer for 1.1 (and thereby 1.2 potentially) may be wrong. That thing is pure pointer arithmetic which [i]should[/i] be well defined really (it would make it point one byte after the array if overflow doesn't happen, whatever that address turns out to be - doesn't need to be memory). There's the problem of overflow though, which depending on how pointers are stored behaves differently (no idea what the standard says about how pointers can be stored).[list]
[*]If pointers are unsigned integers, then it'll just roll over back to the beginning in case of overflow.
[*]If pointers are signed integers, then it's undefined behavior (though on two's complement systems it works the same way as for unsigned integers technically)
[*]If pointers are something else (which was the case for really old systems), then... who knows what could happen, this is even worse than your usual undefined behavior.
[/list]
And even in either case it's still wrong: if the allocated block of memory happens to be exactly aligned to the top of the address space, that would mean p + 10 is overflowed, and thereby we can't rely on the value of that address (in particular, it won't be after p + 9 at the very least, and most likely would end up before it). So the actual defined range in that sense would be p to p + 9, not p to p + 10.

Needless to say, answers 1.2 and 1.3 are both affected by this.

Share this post


Link to post
Share on other sites
Hodgman    51237
[quote name='Sik_the_hedgehog' timestamp='1354779033' post='5007664']
Actually, decided to take a look to it, and I think the answer for 1.1 (and thereby 1.2 potentially) may be wrong. That thing is pure pointer arithmetic which should be well defined really[/quote]Yes, it should be well defined, but it's not ([i]maybe this has changed in C++11, I've not re-checked the spec[/i]). You're allowed to point to "one past the end" of an array, but no further than that.
In practice, it works fine, but a compliant compiler is allowed to do whatever it likes if you ask for a pointer that's "two past the end" of an array. Edited by Hodgman

Share this post


Link to post
Share on other sites
alh420    5995
[quote name='CS_' timestamp='1354759962' post='5007605']
[quote name='SiCrane' timestamp='1354759002' post='5007599']
It's basically inherited from C, which was developed in a time when computers were sufficiently low on memory that they literally couldn't fit all the symbols necessary for a complex program in memory all at the same time and still have room for actual code. Programmers would be able to identify which symbols were really needed and include only those headers.[/quote]

Thank you, though I should have been more clear in indicating that I do understand the reason, but simply cannot stand the additional handling complexity. Of course it has its raison d'etre even today (embedded systems come to mind), but I might see the language in a more positive light if the .h/.c(pp) separation wasn't all but mandatory when targeting systems with plenty of resources. My apologies.
[/quote]

Interesting.
The clear separation of a declaration and an implementation is one of the things I really like with C and C++.
The header file has (should have) everything I need as a _user_ of the class, and nothing more.
Makes the intention of the class very clear, and then I can "hide" the implementation in a cpp file.

Also, this separation is not mandatory, if you want, you can write everything in the same file too. Just gets more messy :P
I really dislike how Java forces me to put everything in the same file...
Very hard to get an overview of the class without additional tools like a smart IDE or doxygen... Edited by Olof Hedman

Share this post


Link to post
Share on other sites
I think the main issue with header files is that you're writing the same stuff twice (e.g. function prototypes vs. function definitions).

Also tried to look up the pointer stuff in C++, and ended up stumbling upon this instead:
http://en.wikipedia.org/wiki/C%2B%2B11#Null_pointer_constant

Gotta admit I didn't think of that one. That's really annoying. (then again, why would you have a function overloaded both to an integer and a pointer at the same time?)

Share this post


Link to post
Share on other sites
mrbastard    1577
People who dislike the compilation model may want to look out for (or even give feedback to the standards committee on) proposals for [url="http://isocpp.org/blog/2012/12/modules-update-video-available-doug-gregor"]Modules[/url]. There have been proposals mentioning partial classes for easier code-gen tooling as well, but I don't know whether this is part of the Modules working group or separate at this point. Edited by mrbastard

Share this post


Link to post
Share on other sites
[quote name='Sik_the_hedgehog' timestamp='1354786764' post='5007694']
Gotta admit I didn't think of that one. That's really annoying. (then again, why would you have a function overloaded both to an integer and a pointer at the same time?)
[/quote]void processListNodes(node *begin, node *end);
void processListNodes(node *begin, int howMany);

I'm not arguing that this is or isn't good design, but it is something many people could plausibly write. Edited by Stroppy Katamari

Share this post


Link to post
Share on other sites
alvaro    21247
[quote name='Hodgman' timestamp='1354782712' post='5007677']
[quote name='Sik_the_hedgehog' timestamp='1354779033' post='5007664']
Actually, decided to take a look to it, and I think the answer for 1.1 (and thereby 1.2 potentially) may be wrong. That thing is pure pointer arithmetic which should be well defined really[/quote]Yes, it should be well defined, but it's not ([i]maybe this has changed in C++11, I've not re-checked the spec[/i]). You're allowed to point to "one past the end" of an array, but no further than that.
In practice, it works fine, but a compliant compiler is allowed to do whatever it likes if you ask for a pointer that's "two past the end" of an array.
[/quote]

All true. Although probably no compiler is going to give you trouble for this one, I was recently surprised when gcc turned this code into an infinite loop:
[code] for (int i = 0; i >= 0; ++i) {
}[/code]

A more naive compiler would stop once i overflows and becomes a large negative number, but what happens after overflow is undefined behavior, so the compiler can assume that it won't happen (if it does, it's allowed to do anything it wants so any behavior is correct) and can therefore optimize `i >= 0' to `true'.

This kind of strict interpretation of the standard by the compiler makers is annoying. For instance, a C++98-compliant compiler can put an infinite loop at the end of a hello-world program, because the standard doesn't specify "finishing in a finite amount of time" as part of the observable behavior of the program. The return value of the program is also not part of the observable behavior, so I think a compiler could stay compliant while returning 3 from main every time, regardless of what the code says. Of course nobody would use a compiler that did that, but it illustrates the point that simply satisfying the standard is a low bar and I expect my compiler to be more reasonable than that.

Share this post


Link to post
Share on other sites
Yeah, GCC generates that kind of code on purpose. Try to check for overflow after the fact (e.g. by checking if the value went negative), GCC will explicitly optimize out (i.e. remove) the check. The reasoning behind this is that "it's undefined behavior so we'll try to break the code so you can't rely on it", except sometimes that can backfire since that kind of bugs on purpose may not pop up in ages, depending on the situation.

[quote name='Stroppy Katamari' timestamp='1354800462' post='5007747']
[quote name='Sik_the_hedgehog' timestamp='1354786764' post='5007694']
Gotta admit I didn't think of that one. That's really annoying. (then again, why would you have a function overloaded both to an integer and a pointer at the same time?)
[/quote]void processListNodes(node *begin, node *end);
void processListNodes(node *begin, int howMany);

I'm not arguing that this is or isn't good design, but it is something many people could plausibly write.
[/quote]
Fair enough.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this