Moderators: phlip, Moderators General, Prelates
Terry Pratchett wrote:The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.
Sc4Freak wrote:I disagree with some of these answers.
Use a smart pointer when you need to express ownership semantics. That is, if you have a pointer which owns the pointed-to object, then you should use a smart pointer. Non-owning pointers should use raw pointers (but prefer references when applicable). In C++11, that means using unique_ptr to express unique ownership (a single instance owns an object) and shared_ptr to express shared ownership (multiple instances own an object).
Take for example a tree structure. A node owns its children, so a node should have a smart pointer to its children. But a child node obviously doesn't own its parent - that would be cyclical. Instead, it should have a non-owning reference back to its parent. In this case, a raw pointer expresses that relationship perfectly correctly, safely, and succinctly.
There are smart pointers that don't express ownership semantics.Sc4Freak wrote:Use a smart pointer when you need to express ownership semantics.
The thing is, there are non-owning smart pointers. In the standard library, weak_ptr is a non-owning smart pointer.Take for example a tree structure. A node owns its children, so a node should have a smart pointer to its children. But a child node obviously doesn't own its parent - that would be cyclical. Instead, it should have a non-owning reference back to its parent. In this case, a raw pointer expresses that relationship perfectly correctly, safely, and succinctly.
Divinas wrote:I disagree. In a single threaded application ,what Sc4Freak is saying is reasonable. But in a multithreaded one, that is not required to be so. If we have a structure A owns B, B keeps a raw ptr to A, if you use the raw pointer while A is being destroyed , the pointer is invalid. If you used a smart weak, non-owning pointer, such as boost::weak_ptr, you're guarded against that.
EvanED wrote:I somewhat disagree. I think this depends on what kind of bugs you like.
The problem is that logic errors in your program can still leave you with dangling pointers. Full use of reference-counted smart pointers avoids this. Do you like that behavior, where errors will often be more evident during development but more dangerous when deployed, or would you prefer that the ownership semantics become a bit unclear, and errors can be masked, but you are cutting out any problems with dangling pointers?
Yakk wrote:The thing is, there are non-owning smart pointers. In the standard library, weak_ptr is a non-owning smart pointer.
Either these pointers are a bad idea, or you should use smart pointers when you aren't trying to express ownership semantics sometimes.
Yakk wrote:If I was building a tree structure and didn't want to do std library QA, and my children needed a pointer back to their parents, I'd use shared_ptr for the pointer-to-child, and weak_ptr for the pointer-to-parent. If I decided that no child should ever have a pointer to an invalid or null parent unless it was a root, I'd add in a root flag, and then assert that the call to get on the weak_ptr always returned non-nullptr so long as the root flag is unset. (I'm only using shared_ptr here in order to permit weak_ptr).
The goal of this would be robustness -- I'm trying to avoid certain classes of error which I find to be common, and using smart pointers to sanity check my code.
Now, the persistent pointer-to-parent would, if nothing goes wrong, be no more dangerous than the weak_ptr, given that I'm asserting things left right and center that the weak_ptr get never fails. But as a side effect to this change I've decoupled the "I am a root" flag and made it explicit, instead of overriding a pointer value so that nullptr serves double duty -- I consider that to be a plus, maintenance wise (while the nullptr as flag is a pretty common idiom, the boolean saying explicitly what it means carries with it a bit more documentation).
void Frobnicate(const shared_ptr<Foo>& foo)
{
foo->Bar();
// Do other stuff to foo (observe, mutate, etc)
}void Frobnicate(Foo* foo)
{
foo->Bar();
// Do other stuff to foo (observe, mutate, etc)
}void Frobnicate(Foo& foo)
{
foo.Bar();
// Do other stuff to foo (observe, mutate, etc)
}You plan for it to be exactly as correct. In small, toy programs, you can probably track down the bugs.Sc4Freak wrote:But for expressing plain old non-owning references like a tree node? I say use a raw pointer. It's exactly as correct, and more efficient.
The goal of this (using a weak_ptr with asserts that it never fails) instead of a raw pointer would be robustness. I'm trying to avoid certain classes of error which I find common, and using smart pointers to sanity check my code.Yakk wrote:The goal of this would be robustness -- I'm trying to avoid certain classes of error which I find to be common, and using smart pointers to sanity check my code.
Now, the persistent pointer-to-parent would, if nothing goes wrong, be no more dangerous than the weak_ptr, given that I'm asserting things left right and center that the weak_ptr get never fails.
But I'm not seeing where the difference is between using a raw pointer vs. a weak pointer in this case. I don't think I'm understanding your example - what, if anything, has weak_ptr actually bought you here?
sourmìlk wrote:So, smart pointers: When should I use them? Should I ever not use them?
I'm not asking about when to use specific kinds of smart pointers. I understand that. I'm wondering how often I should use them, if at all, or if at all I shouldn't ever use them.
EvanED wrote:Sc4Freak wrote:I disagree with some of these answers.
Use a smart pointer when you need to express ownership semantics. That is, if you have a pointer which owns the pointed-to object, then you should use a smart pointer. Non-owning pointers should use raw pointers (but prefer references when applicable). In C++11, that means using unique_ptr to express unique ownership (a single instance owns an object) and shared_ptr to express shared ownership (multiple instances own an object).
Take for example a tree structure. A node owns its children, so a node should have a smart pointer to its children. But a child node obviously doesn't own its parent - that would be cyclical. Instead, it should have a non-owning reference back to its parent. In this case, a raw pointer expresses that relationship perfectly correctly, safely, and succinctly.
I somewhat disagree. I think this depends on what kind of bugs you like.
The problem is that logic errors in your program can still leave you with dangling pointers. Full use of reference-counted smart pointers avoids this. Do you like that behavior, where errors will often be more evident during development but more dangerous when deployed, or would you prefer that the ownership semantics become a bit unclear, and errors can be masked, but you are cutting out any problems with dangling pointers?
You, sir, name? wrote:EvanED wrote:The problem is that logic errors in your program can still leave you with dangling pointers. Full use of reference-counted smart pointers avoids this. Do you like that behavior, where errors will often be more evident during development but more dangerous when deployed, or would you prefer that the ownership semantics become a bit unclear, and errors can be masked, but you are cutting out any problems with dangling pointers?
Huh? If A owns B, and B refers to A, then if A expires, B expires as well (because A owns it) and no pointer is left dangling. Safe as houses.
EvanED wrote:You, sir, name? wrote:EvanED wrote:The problem is that logic errors in your program can still leave you with dangling pointers. Full use of reference-counted smart pointers avoids this. Do you like that behavior, where errors will often be more evident during development but more dangerous when deployed, or would you prefer that the ownership semantics become a bit unclear, and errors can be masked, but you are cutting out any problems with dangling pointers?
Huh? If A owns B, and B refers to A, then if A expires, B expires as well (because A owns it) and no pointer is left dangling. Safe as houses.
You've assumed the code is right. You can't talk about what happens if it's wrong if you take that as an assumption.
I'm saying: A owns B. C (perhaps a function, as a parameter) gets a non-counted reference to B. A expires, which expires B. C is left with a dangling reference.
Or say you have a tree. A has a child of B (ref counted), and B has a raw pointer back to A. Now you just want the B subtree, so you make a copy of B's shared_ptr (incrementing it's count to 2) then delete A (which in the process decrements B's count back to 1). But you forget to reset B's parent pointer to NULL. That's your dangling pointer. It's an error that would have been caught with something like weak_ptr, but of course won't be caught by the raw pointer.
The existence of the possibility of a crash in C++ does not mean that avoiding a crash is pointless.You, sir, name? wrote:The same thing could be said about passing a pointer to an element in an array to a function, and then deallocating the array. You can't guard against idiots shooting themselves in the foot.
You, sir, name? wrote:The same thing could be said about passing a pointer to an element in an array to a function, and then deallocating the array. You can't guard against idiots shooting themselves in the foot.
You, sir, name? wrote:Or say you have a tree. A has a child of B (ref counted), and B has a raw pointer back to A. Now you just want the B subtree, so you make a copy of B's shared_ptr (incrementing it's count to 2) then delete A (which in the process decrements B's count back to 1). But you forget to reset B's parent pointer to NULL. That's your dangling pointer. It's an error that would have been caught with something like weak_ptr, but of course won't be caught by the raw pointer.
This is leaving the pattern altogether. A should have an unique_ptr to B if it owns B.
I think that's probably overkill though, given that prepared statements already essentially eliminate any chance of SQL injection. There's no need to try and implement a type safe system to do this.Yakk wrote:SQL injections can be seriously mitigated with type-safe improvements. Getting it to happen is hard.
Basically, a string that comes from outside of the compilation unit (be it a config file, user input, or the like) should be a different type of string than a compiled in string.
data Predicate = OR [Predicate] |
AND [Predicate] |
FIELDSTRPRED Field (String -> Bool) |
FIELDINTPRED Field (Int -> Bool)
newtype Entry = ...
newtype Table = TABLE String
newtype Field = FIELD String
newtype Database = DB (Server, User, Password)
type Server = String
type User = String
type Password = String
query :: Database -> Table -> Predicate -> IO Entry
query (DB (s,u,p)) = connect s u p where ...
...
gameLookup = query gameDBInfo
playerLookup = gameLookup playerTable
...
showStats name = playerLookup (FIELDSTRPRED playerName (== name)) >>= printStats
statsRequest = getLine >>= showStats All Shadow priest spells that deal Fire damage now appear green.
Big freaky cereal boxes of death.
All Shadow priest spells that deal Fire damage now appear green.
Big freaky cereal boxes of death.
Users browsing this forum: No registered users and 11 guests