Bad Advices

Pages: 12345
@ Catfish4, one is not easier over the other nor did I ever say that.

Nor did I ever say you said that. I said that.

Are arrays harder to use than vectors? Yes.
Are plain pointers harder to use than smart pointers? Yes.

(It would be amusing if you'd bring up C++98 to prove me wrong.)

C++ isn't a language for beginners but telling someone they should do something that is probably bad practice doesn't help.

You mean giving bad advice makes matters worse because C++ is hard? That's true for any language, in general.

Don't hide behind optimization now, after you started this thread about bad advice being given to beginners.

C++ offers improvements over C. Might as well promote their use, no?
Because what matters for a beginner is that they become proficient at C++ first, then experts in C++ and optimization second.
closed account (S6k9GNh0)
Given that std::array have most of the exact same semantics of a vector, that doesn't even make sense to say that one is more difficult to use than the other. One includes facilities to change size in a dynamic way, one does not. I cannot agree with what you're saying.

EDIT: Smart pointers require an understanding of how each type of smart pointer works... while both require an understanding of pointers... how is a smart pointer "easier to use"?

Catfish4 wrote:
Don't hide behind optimization now, after you started this thread about bad advice being given to beginners.


You're become quite irritating with you're wording. I'm not hiding from anything and provided good reasoning behind my thoughts. The reason I recommend an alternative to shared_ptr isn't optimization, it's because I don't believe it entirely fixes what it's supposed to fix in practice i.e. has nothing to do with whether its faster or not.
Last edited on
It looks like Catfish4's "array" is a plain C array and computerquip has a std::array.

"Plain pointer is harder" probably refers to:
1
2
3
4
5
6
int main () {
  int * bar = new int;
  // use bar
  delete bar; // explicit deallocation required
  return 0;
}
vs
1
2
3
4
5
int main () {
  almost_smart_pointer_type<int> foo (new int);
  // use foo
  return 0; // automatic deallocation
}



The general advice is thus:
Always use the most appropriate tool for the job. In order to do so you have to know every detail of every tool.
Could be classified as "bad advice".
closed account (S6k9GNh0)
Prime example of the requirement of needing to understand how the container or library you use is cin streams. How often do we see people who are new to C++ incorrectly. Instead of cin >> myInt;, we often suggest stringstreams (despite them being slower) or lexical_cast along with string::getline().

I can understand why we suggest the slow procedure above because it works fine for smaller programs but I wouldn't suggest it so much in larger programs. Also, it's less error prone and the result is generally exactly what the user is wanting (albeit slow).

You do need to understand every tool you use to achieve the result you want, at least to the extent of the interface that it provides for what you're wanting.
#3 can be fixed with a unique_ptr.


I know that. But you were discussing how shared_ptr stacks up against raw pointers. #3 is a problem with raw pointers that shared_ptr solves. Yes, unique_ptr also solves it.

#1 is only half fixed given that you'll have to figure out whether every reference is dead in order to ascertain that it's deleted.


That's not true. If you have a shared_ptr that's non-null, then you can be 100% sure it's a valid pointer.

It's technically possible that won't be true, but you would have to deliberately sabotage yourself and/or be passing around raw pointers and manually deleting them for this to happen. So again it comes down to being a problem with raw pointers and manual deletion.


EDIT: Sorry, I thought #1 was a different item. You're right. But that's a problem with the shared ownership paradigm, not really with shared_ptr. You'd have the same problem with raw pointers sharing ownership.

So yes... shared_ptr only half fixes it. But raw pointers are no more "fixed" than shared_ptrs are.

#4 can easily be fixed by setting the pointer to NULL after deletion which can be wrapped in a macro/function instead of using unsafe delete. If different resources hold the same pointer (meaning one does not hold a reference of another pointer, but a true copy of the pointer) then that is "bad design".


Yes that is arguably bad design. But it's still a problem that raw pointers have that shared_ptr does not.

Remember: shared_ptr is for shared ownership. If you think shared ownership is not applicable or is a bad design for a particular problem... then shared_ptr is not the right tool. But in instances where you need shared ownership, shared_ptr solves all these problems whereas raw pointers are extremely difficult to deal with.

Holding a reference to another pointer and using a function rather than delete will prevent deleting that pointer and require you leave it up to whatever owns that pointer to delete it, at the cost of nothing.


It adds the cost of another indirection... which is not nothing. If you're nitpicking over the performance of reference counting... I'm going to nitpick over the performance of extra levels of indirection. In fact, the extra indirection is probably slower than doing the reference count for this situation... as you only reference count on construction/destruction, but you do the extra indirection every time the reference is accessed.

But anyway, ignoring the possibility that this may be slower, it still only works until the original pointer goes out of scope.. then you have dangling references. So this does nothing to solve the problem.

And of course you could just keep a reference to the shared_ptr as well... which is exactly the same syntactically... costs the same amount.... but also means you don't have to manually delete or remember to call a "safe delete" function.

Also... the solution of calling a separate safe delete function is introducing yet another way for the programmer to screw up. So that might be a 5th problem with raw pointers that shared_ptr solves: "5) forgetting to NULL the pointer after deleting it"






EDIT 2:

Let's come back to the original purpose here, though.

You're saying newbies should not just throw shared_ptr everywhere. I agree with that. What I don't agree with is saying that newbies should use raw pointers for memory management. In fact I don't think anyone should use raw pointers for memory management in C++. There is no advantage to it, and it's extremely error prone (especially with newbies).

So rather than discuss "shared_ptr vs. raw pointer".... I'd like to shift this back to a more general "raw pointer vs. smart pointer" debate.

Can you give me any situation where maintaining ownership with a raw pointer provides benefit over doing so with an appropriate smart pointer or other RAII object? Because I can't think of a single one.
Last edited on
closed account (z05DSL3A)
computerquip wrote:
Some things I really don't like about C++ is some of the rather redundant things I see people suggest to people who are new.
Sometimes you have to be given the nearly true or the not correct but you are not ready for the truth advice, it is the way it is all over. How many people think that centrifugal force is real?

My biggest problem is people giving advice such as 'use std::string instad of a char[]' or whatever when they haven't even found out the reason the OP is using said. It is not necessarily that the advice is bad, just that it is not always warranted and sometimes confuses the issue.
The issue with learning C++ seems to be with C. People come in and say "Oh well they're pretty much the same, but C is simpler so I'll learn C++ by learning the C stuff first."

No other similar language has this issue. People go learn Java, they learn Java and the proper way to program with Java. Same goes with C#, Objective-C, etc. We should be telling people to learn by using the C++ features, even if they're more complicated to use than C.
closed account (S6k9GNh0)
I'm not defending the raw pointer to make things clear. In a small program, I do use raw pointers because the benefit of using alternative is more hassle than it's worth. In a larger program that uses an unknown amount of memory, I would probably use memory pools or even a garbage collector. I've used apache's memory pools along with some of their utility functions from the apr library with surprisingly good success. Allocation times were wayyyy better than randomly using new but the problem ends up always being when to deallocate...

EDIT: Memory pools end up being quite a bit safer since you know where the memory always is. Of course you can lose references to a memory pool but uh... it's rather difficult to do.
Last edited on
In a small program, I do use raw pointers because the benefit of using alternative is more hassle than it's worth.


I guess we'll just have to disagree on this one.

I just don't see where the hassle is, honestly. More typing?

1
2
3
4
5
int* foo( new int(5) );

//...

delete foo;

vs
1
2
3
std::unique_ptr<int> foo( new int(5) );

//... 


I guess the unique_ptr version is a whole 6 characters longer (not counting whitespace). But if you are manually null'ing your pointer after deletion then it's actually less typing.

WISEASS EDIT:
or if you want exception safety:
1
2
3
std::unique_ptr<int> foo( new int(5) );

//... 

vs
1
2
3
4
5
6
7
8
9
10
11
int* foo( new int(5) );
try
{
    //...
}
catch(...)
{
    delete foo;
    throw;
}
delete foo;


/WISEASS EDIT

In a larger program that uses an unknown amount of memory, I would probably use memory pools


Putting memory in a pool doesn't stop it from leaking. You still need to destroy individual objects when you're done with them. You also can (and should) still use smart pointers with memory pools.

Only way a pool might help with this is if you want to destroy ALL objects in the pool at once... and that's usually a terrible idea because it'll leave you with dangling pointers everywhere.

Pools are primarily to solve the issue of memory fragmentation and allocation speed, which are separate topics entirely.

or even a garbage collector


You'd prefer GC over shared_ptr? Now I'm very confused. Pretty much all your complaints with shared_ptr apply to GC tenfold.
Last edited on
closed account (N36fSL3A)
But I like raw pointers.
Personal preferences aside... my point is that logically there is no reason why you should use a raw pointer to own dynamically allocated memory when smart pointers are available.

The only thing I've read in this thread which says otherwise is computerquip claiming "they're not worth the hassle"... without any indication of what that hassle is.

I suspect computerquip just has a personal bias and/or is just so used to using raw pointers that he doesn't want to change his practices -- and is merely trying to rationalize that position.


Which is fine. You can code however you want. But since the topic was about what we should be telling newbies to do.... there is EVERY REASON to tell them to use smart pointers.
closed account (S6k9GNh0)
No, I like unique_ptr and even weak_ptr in conjunction with shared_ptr when it's in use. I've said this numerous times through out the topic (lol). Putting memory in a pool *can* help leaking since you can track where memory is allocated from, prevent fragmentation, and have control over every location the pool manages. It's rather close to a garbage collector in the sense that you can lose a reference to a pointer completely, clean the pool, and there is no longer a leak.

I've indicated that the hassle is that it requires understanding where the references die and making sure that they're dead to ascertain that the memory died.

I'm rather tired of people assuming, and actually try and tell people a distorted view of my opinion. I've already laid out the reasoning, you can disagree with the reasoning, but do not say that I've not provided reasoning.

Also, I've not told people to not use smart pointers. I've continuously said that my only issue is with shared_ptr which often acts as a replacement for memory management or a garbage collector. Quit saying that I have an issue with smart pointers. That's incorrect yet its been stated explicity by others that for some reason, I don't like smart pointers. Enough.

There is NOT every reason to tell people to use shared_ptr for everything. All other facilities to help with dynamically allocated memory is perfectly fine and I've not said otherwise. Stop saying I have.
Last edited on
You are correct computerquip. I apologize. Looks like I made some incorrect assumptions about what you were talking about.

I think we're more or less in agreement, we just are bickering about some of the finer points. So I'll just bow out of this now.

Sorry again. I guess I just put blinders on. =x
computerquip wrote:
I've indicated that the hassle is that it requires understanding where the references die and making sure that they're dead to ascertain that the memory died.


Just wondering why that is: Isn't the whole point of smart pointers so that you don't have to do that? Or are there some other complexities that my relatively low level of experience make me ignorant of?

Some other thoughts:

I find it a little confusing when I read Herb Sutter saying: "In C++03, we taught match every new with a delete. In C++11, we teach use smart pointers exclusively - never use new or delete ever again". I may not have quoted that exactly, but I am sure that was the gist of what he said. But then we have Cubbi saying this (gained from years of professional experience, I am sure):

Cubbi wrote:
I like it quite a bit, it has some awesome features, but I've only willingly used it once in production so far (it was called boost::shared_ptr then). It is very hard to come by a valid design that involves shared ownership. I agree that adivising shared_ptr for any sort of "memory management" is usually wrong (and smells of Java)


@Cubbi
Apologies in advance - I am not trying to have a go by quoting expert authors at you, just trying to increment my education a little. Scott Meyers said this in "Effective C++, 3rd Ed".

Scott Meyers wrote:
For example STL containers require that their contents exhibit "normal" copying behaviour, so containers of auto_ptr aren't allowed.


He then introduced the concept of shared_ptr. This seems to be the main motivation for using a shared_ptr. Later on, he talks about copying scenarios, with these possibilities:

1. Prohibit copying;
2. Ref count the underlying resource;
3. Copy the underlying resource;
4. Transfer ownership of the underlying resource.

I notice you said "shared ownership", but how much truth is there in saying that there seem to be a number of valid needs for copying?

So, why does it seem that copying is bad design? Or how do you manage to come up with a design that doesn't involve copying? Does that mean using references (rather than raw pointers) and accounting for every situation to make sure delete is called?

I guess that language features get invented, then professional coders discover all the intricacies of them, and come up with exceptions for their use, or a variety of appropriate uses. This is the difficult thing when still learning (like me ) and I imagine for giving advice too.
TheIdeasMan wrote:
in C++11, we teach use smart pointers exclusively - never use new or delete ever again". I may not have quoted that exactly, but I am sure that was the gist of what he said. But then we have Cubbi saying this

I haven't written a delete in production code even once since about 2007 myself, there's no contradiction (there was a fair share of news though, when I was using a library without an std::make_unique equivalent, but Herb kinda treats C++14 and C++11 as a single release)

He then introduced the concept of shared_ptr.

I see your Meyers and raise you a Stroustrup
Stroustrup 2012 wrote:
Use shared_ptr as a last resort


More seriously, though, the need to build safe containers of pointers to dynamic objects (which was something std::auto_ptr wasn't designed for) did motivate people to use counted pointers, but boost.ptr_containers were the better solution, and C++11 made it a non-issue by introducing move semantics and unique_ptr.

I guess that language features get invented, then professional coders discover all the intricacies of them, and come up with exceptions for their use, or a variety of appropriate uses

With C++ most features evolve, rather than get "invented". Already in pre-standard days of 1992-1998 everyone who was serious about C++ had their own scoped, ref-counted, ref-linked, intrusive, deep-copying, copy-on-write, and other pointers of varying degrees of smartness, even though they were rejected by the standards committee in 1994 (except for the destructive-copying auto_ptr). Scott Meyers was describing some of the existing practices and their trade-offs and rationales (1st edition was in 1998 and covered smart pointers). Only a few smart pointer designs stood the test of time.
@Cubbi

I see your Meyers and raise you a Stroustrup


lol - very good!!

Thanks for the very informative post (as always).

Maybe the real difficulty for learners (and dabblers like me) is trying to keep up with all the commentary of what is best practice. And that there seems to be no "one size fits all" situation - there are scenario's where something is valid, but others where it is not. And advice given by top authors may be contradicted in a relatively short space of time.

With Stroustrup's comment, I would be interested to know the detail of what he said. Was it in one of his books? I will google & see how well that works.
closed account (S6k9GNh0)
http://programmers.stackexchange.com/questions/133302/stdshared-ptr-as-a-last-resort

http://stackoverflow.com/questions/3628081/shared-ptr-horrible-speed

http://www.swageroo.com/wordpress/c11-standard-what-are-smart-pointers-good-for/

Top 3 Google results
Speed is not everything. In fact most of the time it's only a minor issue.

And as I said before you can always continue with a raw pointer.
For that (rare) scenario above (path find and such)

In other issues shared_ptr helps a lot.

Factory: you can create an object and give it away without caring
Shallow copy: is allowed with shared_ptr
Uninitialized pointer: not an issue anymore

std::shared_ptr as a last resort?
Complex or unclear ownership models tend to lead to difficult to follow couplings of different parts of the application through shared state that may not be easily trackable.
To hold this argument against shared ptr is somewhat strange. With shared ptr ownership is not an issue. I'd say that this guy has more an issue with reasonable design than shared ptr.
You can always pass a const shared ptr: std::shared_ptr<const CData>

so all in all I'd recommend to use shared_ptr unless you have good reason not to
> Speed is not everything. In fact most of the time it's only a minor issue.

Yes, the issue is not primarily one of performance.
As Stroustrup points out, yet once again, (in a post in another C++ forum):
I do not (emphatically not) consider unique_ptr vs shared-ptr a simple optimization (even though there are major performance differences).The distinction is logical. What I'm saying is that shared ownership is less fundamental that(n) people think - in fact they try hard not to think about ownership at all and pay for that in program complexity, maintenance cost, as well as performance.

Post #35 in http://forums.devx.com/showthread.php?173919-C-0x-rationales/page3


> Complex or unclear ownership models tend to lead to difficult to follow couplings
> of different parts of the application through shared state that may not be easily trackable.

>> To hold this argument against shared ptr is somewhat strange.
>> I'd say that this guy has more an issue with reasonable design than shared ptr.

I concur with this assessment. Designs with pervasive shared ownership of resources (particularly when they are saddled with unclear ownership models) tend to be brittle, bug-prone, inefficient designs that are hard to debug and hard to maintain. Shared pointer is not the causes of the problem here - such a design would have been brittle, bug-prone etc. with or without the use of shared pointers.


> so all in all I'd recommend to use shared_ptr unless you have good reason not to

Don't agree with this, though. Even if performance was not an issue, the problem with using a shared pointer where a unique pointer would be appropriate (no shared ownership is required) is that, over a period of time, it seduces one to:
try hard not to think about ownership at all and pay for that in program complexity, maintenance cost, as well as performance.

Last edited on
In terms of dynamic memory I use whichever solution is closest to RAII.

Other people swear by garbage collectors, I swear by RAII :)
Last edited on
Pages: 12345