I've googled the issue and read a few pretty abstract explanations.
I was hoping you could give me a few solid examples for when one should use pointers.
Thanks
Printable View
I've googled the issue and read a few pretty abstract explanations.
I was hoping you could give me a few solid examples for when one should use pointers.
Thanks
Just to check: what material (e.g., books) are you using to learn C++ programming? How much of the material have you learned?
No books, but I covered every single word here:
http://www.cplusplus.com/doc/tutorial/
In case you're afraid I'm too new to programming to start learning this kind of stuff, I also have about 8 years of experience in ActionScript 3.0 (OOP).
1) In C++, you cannot achieve polymorphism without pointers (or references). Polymorphism is one of the cornerstones of OO programming, and you can't do that in the C++ language without using pointers/references.
2) The allocator functions (new, malloc(), etc.), plus many standard functions return pointers. So you have no choice but to use pointers since that is what is returned to you.
3) Calling a function that changes its argument(s) value(s) internally inside the function can require you to pass an address (i.e. pointer).
So right there, you cannot avoid using pointers in those 3 scenarios. Any other scenario, pointer usage should be minimized in a well-written C++ program (with the exception of smart-pointer usage).
Regards,
Paul McKenzie
1) Do I have any real reason to use pointers over references here? So far I managed to pull off everything I tried with references only, maybe it could have been done more efficiently though using pointers I really have no idea.
I see, so those are the scenarios I need to be looking for? Other than that should I just use references everywhere?
One rule of thumb concerning efficiency and C++:
Unless you're writing a function that implements a general algorithm, trying to guess in C++ what code is more efficient than another code will yield you, for the most part, wrong answers.
Too many persons new to C++, or persons who know another language but think they know C++ well enough, almost always guess wrong as to what code is more efficient than another piece of code. One main reason is that the C++ code we see is usually not the same code after the compiler finishes its optimization process. Yes, the code will execute properly, but the compiler has done "tricks" to make it more optimal than what the original source code suggests.
Second, as far as using a pointer, maybe the function wants to accept NULL as an argument. You can't legally pass NULL references in C++, but you can pass NULL pointers.
Regards,
Paul McKenzie
Duly noted. Thanks alot.
nuzzle - Yes I did like OOP, what do you mean by using the whole language and not just a fraction? Got any example for that perhaps?
D_Drmmr - Okay I understand.
You must start using pointers! :) and the heap.
I found myself in your situation a few years ago but coming from another OO language - Java. C++ is very versatile and I've set up a scheme based on smart pointers that quite closely mimics the memory management of Java. I'm sure you could do the same for ActionScript. I haven't used it but had a quick glance and it looks a lot like Java. And by the way, smart pointers preferrably are passed by reference so you can have it both ways, both reference and pointer. :)
People are going to warn you for this approach though. Conventional wisdom has it that one should use each language for what it is, not pretend it's something else. But that's not what I'm doing. I'm just taking advantage of the exceptional powers of C++ to mold it to my liking and it works fine for me. I haven't bothered to even learn all that boring and complicated value semantics stuff everybody think they must be doing. My heap allocated objects are prevented from being copied by design so that's not even an issue (and my value objects are always kept so simple they can do with default copy construction and assignment operation).
You won't be able to do what I suggest immediately but have it in mind and you'll find a way eventually.
Java is indeed very similar to ActionScript 3.0, I've been using it as well. There are minor syntax differences but overall once you know one it's very easy to learn the other.
What are those boring and complicated value semantics you're talking about?
The attitude you have sounds very tempting, I do want to make sure though that I can understand other people's code.
Is this conventional wisdom of C++ OOP programmers? It simply sounds like you're going full OOP on C++. I can't see what's wrong with what you're saying but hey, I just got here :)
There are many ways you can solve a particular problem in c++
As a general rule concerning pointers and references...
Use references. Use pointers only when references won't do. If you do use pointers, you probably want to hide that implementation in a single function or in a single class as a private/protected member. If you have to use pointers, it's probably a good idea to manage them through one of the various smart pointer classes.
All the above rules have their reasons. As a whole, pointers tend to confuse novice programmers, and even experienced programmers will at times be confused. If you have a pointer you get confusions about: who's responsible for filling in the pointer, is it a pointer to static data to local data or to allocated data will the pointer still point to something under all conditions, does it HAVE to point to allocated data, who's responsible for cleaning up the data if it was indeed allocated, if you cleaned up the data, what should the pointer point to ?
You can avoid a lot of those confusions/headaches by avoiding pointers. It's also why we have container classes like vector/list/map/queue... For one they relieve you of the work to write it yourself, but it also hides a lot of the "pointer mess" behind well tested classes. And this is a good approach for yourself too (see the above general rule), if you have to use pointers, hide the "pointer mess" behind a clean interface of your own. The fewer places in your code you have to deal with pointers, the fewer places in your code you'll have to worry about them.
Everything you can do with a reference you can do with a pointer (except when you need a type difference between the two) but not the other way around, which is why you can't always avoid pointers.
I understand. I imagine there'll be cases where I'll have to expose my pointers, such as when a factory creates and returns a new product (OOP). Will smart pointers (scope) be good for that?
Yes, when you have a factory it is good practice to return a smart pointer (either shared_ptr if you need reference counting or (normally) a unique_ptr).
I think it's good to make a clear(er) distinction between using pointers and managing objects that are allocated on the heap. For the latter, you should use smart pointers whenever possible, because they make life much easier. However, there is nothing wrong with using raw pointers when you are not concerned with managing memory and a reference won't do. E.g. if you need to create a wrapper for some object, it may be useful to store a pointer to the wrapped object. This will make the wrapper assignable, which it would not be had you used a reference. Another example is passing an optional argument to a function. It perfectly OK to use a raw pointer for that, where the value NULL indicates that the argument is not provided.
Got it.
If I understand correctly on the heap means allocating memory dynamically. What does using multiple heaps mean? Sequences of allocated memory with distance between them? (I hope I'm making sense, I'm completely new to memory allocation)
No. on the countrary. you should avoid putting large objects on the stack (function local data). As this could cause you to overflow the stack.
The heap is the right way to go for any non-static non-const objects of any reasonable size.
I don't quite understand your argumentation.
Do you mean it's the size of an object that determines whether it should be stack or heap allocated? If an object is big enougt to risk overflowing the stack then the heap is fine but if it's sufficiently small then as a general C++ rule the heap should be avoided?
I think the major source of inefficiency of heap-managed objects is not much the allocation process per-se, but rather that such an approach usually implies a run-time binding of the abstraction entitities. This means poor scalability with respect to the time/space overhead versus the number of "interacting" entities in the design.
Conversely, value-based abstractions ( eg. abstractions where entities are modeled by regular or semi-regular types, that is types behaving as integer-like objects ) allow compile-time binding enabling potentially optimal space/time performance without sacrificing high-levelness.
Moreover, the resulting code will look even higher level because every object will be directly visible to the programmer in the code in a natural way, rather than being hidden behind a further level of indirection.
Of course, I'm not saying that the traditional way of managing OO hyerarchies in C++ should be abandoned ( and I'm very fond of it too :) ), especially because there are so many other things to consider beyond efficiency before coming to such a conclusion.
But if one needs a truly scalable solution ( eg. library writers ) then different approches are worth considering IMO.
This is one of the many long-lived myths about OO and virtual functions and it simply isn't true.
It's because a virtual call resulting from an OO design isn't just a call, it's also a selection. To accomplish the same without a virtual call you need to first select which direct call to make and then perform it. And I can assure you that a compiler generated virtual call is always going to be faster than the if-then-else chain or switch statement or table-lookup or whatever an application programmer might attempt to emulate it.
Furthermore the difference in modifiability is striking. When you add functionality to a well-designed OO application you do that by adding new classes. Old code isn't touched. This even has a name. It's called the Open/Closed principle. With your procedural approach on the other hand the whole application must be reviewed and every dependency found. An extremely error-prone process.
And it doesn't stop there. Procedural designs and even template based designs tend to be very monolithic. Everything is connected and brittle. Rather than touching existing code people take the safer bet and start copying code modifying the copies. This leads to code bloat and introduces even more dependencies. What was once only very hard to manage evolves into an untouchable monster over time.
So don't tell me your approach is "truely scalable". Not in practice and not for systems of some size.
Bjarne Stroustrup knew what he was up to when he introduced virtual functions. In The Design and Evolution of C++ he writes (p. 49) that with good reason he doubted his ability to teach people how to use them and even more so his ability:
"to convince people that a virtual function is as efficient in space and time as an ordinary function as typically used".
And he was right. Today, 20 years later, people appearantly still can't get the head around this fact.
tell me, where did I say that the reason of non-scalability is the virtual call ??? the point is exactly to avoid that "selection" in the first place, unless it's truly required to happen at runtime. I just said that traditional OOP promotes runtime binding of entities at run time even when not necessary or intended by the programmer.
is the STL "connected", "brittle" and "monolothic" ???
"sort of" :) ALthough you're conclusion is the other way around. it's not that the heap should be avoided, but stack should be prefered.
From a performance P.O.V. function local data is prefered. Other than global data, this incurs the smallest amount of overhead.
if you allocate an object, there is an overhead for allocating the object, and eventually deallocating the object. There is a non-zero performance cost associated with (de)allocation. There is also a slight increase in code for the allocation and deallocation. And there may or may not be an increase in code size (with a slight performance loss) each time you use the allocated object because of the pointer indirection.
That allocation cost matters is even visible in the STL where std::string is optimised to havign an internal buffer rather than an allocated one for small sizes.
For most functions, the overhead won't matter much because the function itself won't be called enough times to make a noticable difference. But in a critical function, the allocation/deallocation can make a big difference. This can be particularly true in a multithreaded application where a thread may have to wait on other threads to complete their allocation/deallocation request. For obvious reasons, the heap access needs to be synchronised afterall.
Other than that there is also the ease of code writing.
if you have a function and this function needs an int... would you just write an int ? or allocate an int* ? What about if you needed a std::string do you just use the string or allocate an std::string* ?
unless you "really had to" for some reason, no sane programmer will do an allocation for this.
so yes, the general rule would be "use function local data for everything". And you would except that rule for big objects that would overflow the stack (or significantly contribute to it), and for objects that need to be dynamic and thus mandate allocation.
You said runtime binding causes non-scalability and in common C++ lingo "runtime binding" refers to late binding of virtual function names. If you deviate from that usage you'll have to be clear about it.
So what do you mean by late binding? And while you're at it, how does it cause non-scalability?
You're just relaying urban myths about OO but I'll go into that when you've clarified what you mean by late binding and how this relates to the non-scalability you claim it causes.Quote:
the point is exactly to avoid that "selection" in the first place, unless it's truly required to happen at runtime. I just said that traditional OOP promotes runtime binding of entities at run time even when not necessary or intended by the programmer.
I don't see how the STL standard has anything to do with this discussion.Quote:
is the STL "connected", "brittle" and "monolothic" ???
Or are you referring to a specific STL implementation? Well, even if you located one of excellent quality that wouldn't prove anything about template based systems in general. As I said, they tend to be very monolithic with the negative consequences I mentioned.
maybe it's a terminology problem, but to me, "connected", "brittle" and "monolithic" are properties related to a (library) design, not its implementation, sorry if you meant something else. The STL is an example of a good template based library design that is neither "connected" nor "brittle" nor "monolithic". And there are many other existing field-proven examples.
yes, indeed "the cost of late binding of virtual function names ( in the usual C++ OOP practice )" is not the same of the "cost of a virtual call". The latter is just the difference in space-time overhead of a virtual call VS a simple function call; the former includes the latter plus any cost implied by the use of late-binding VS static-binding of function names in a typical OOP design. Maybe, it's again just a matter of terminology so sorry if I haven't been clear on this.
in order to avoid further confusion, let's agree on what "traditional C++ OOP" is ( from now on, C++OOP ). Obviously, there's no such a universally defined thing so you'll forgive me if I make a hopefully innocuous semplification, by saying that a "C++OOP" design is a set of non-copy/move-constructible/non-copy/move-assignable classes related by public inheritance and respecting the liskov principle. Moreover, let's assume all instances of such classes are accessed/managed via (smart)pointers. The classes are the "entities" of the design, "relations" between entities are modeled by the classes interfaces ( both member and free ).
let's also agree on what a "template based design" ( from now on, C++TB ) is: again simplifying, let's say it's a set of "concepts" ( constraints on types and type templates ), a set of "models" ( types/type templates modeling some concept ) and a set of "algorithms" acting on concepts ( ordinary functions and metafunctions, including concept checking/model generation facilities ). The "entities" of the design are the concepts and the models, "relations" between entities are modeled by the set of algorithms and the types interfaces ( both member and free ).
Finally, by "non-scalability" I'm referring to the space-time overhead asymptotics of a C++OOP design compared to an equivalent C++TB design, with respect to its number of entities and relations. Two such designs are considered equivalent if one can map entities and relations of each other in a one-to-one fashion.
Now, if we reasonably agree on the problem statement, we can start arguing ..., do we agree ? :)
PS: Note that I never claimed that everybody should care about such a scalability issue. Indeed, I fully agree with your Bjarne's quote about "typical" efficiency. I'm specifically thinking at library writers whose abstractions are expected to efficiently work for a wide use case scenarios ... clearly, such programmers represents a possibly small fraction of all programmers. Their work is nonetheless important, though.
Okay, I get your point. And I agree, but only when the choise is devoid of any design considerations. Then the principle simply is the good old Occam's razor; to strive for the simplest solution, and the stack definately is simpler than the heap. But the performance gap has constantly been narrowing. And old truths may quickly become outdated. For example what you say about the heap and multithreading is likely to be outdated post C++ 11.
In my view a more important principle is what Donald Knuth famously expressed as: Premature optimization is the root of all evil! If you feel the heap is the better design choise then go for it. In a broader context it may even be the more efficient one.
There will ALWAYS be a non-zero cost associated with allocation/deallocation. Multithreading may make the effects more dramatic due to synchronisation needs.
I'm not sure how or even if at all C++11 can make any change to this. how would a compiler know that
automatically needs to use a different heap depending on the thread this was called from? And if it can decide... this then has an "undefined" allocator. so what happens with the type ? is this a plain std:string or is if different from the "same" std::string made from another thread. Right now, the explicit allocator means they're different types.Code:std::string* foo()
{
return new std::string("foo"); // Which allocator ?
}
ok, so maybe it uses thread local storage to do this, but a compiler cannot guarantee this will even work. What about deallocation. Who's the guarantee that it'll be deallocated on the same thread ? C++11 cannot remove the need for a heap to need synchronisation (which you CAN do for extra performance with a custom allocator if you know it won't ever be used cross-thread).
maybe it could decide on a heap per allocation/new as from what thread initiated it, but I.m.o. that would probably be more of a problem as a general approach than an actual benefit. Maybe there is a good reason for not wanting separate threads, so how do you override the default behaviour ?
So IMHO, I doubt any "automatic" system for allocators for multithreading is going to happen. Although I can see them adding custom thread-based allocators STL alongside the current single one, but you'll have to explicitely define your classes to use them.
Then again... i might be wrong :)
regarding the hypothetical thread-friendly heap I agree with you, it seems possible only as a compromise rendering some use cases sub-optimal ( which is against the C++ philosophy, IMHO ).
That said, c++11 does provide many enhancements to allocators, most notably: allocator_traits easing the creation of custom allocator, full support to stateful allocators, the scoped allocator adaptor allowing "nested" allocations in containers and finally the template alias syntactic sugar thanks to which the allocator can be specified just once in a single scope. So, writing and using "plug-and-play" default allocator replacement has never been easier :)
Well, then you've learned something new because these words are commonly used about designs in general, but less so in the context of design of standards. In fact I don't think I've ever a heard a standard being called connected, brittle or monolithic, even though I suppose standards can be all of that.
I think you're making a mountain out of a molehill. It all boils down to how two different kinds of polymorphism are supported and commonly implemented in C++, namely subtype and parametric. You claim parametric scales better than subtype but you haven't explained how?Quote:
do we agree
My position is that they scale identically. Even though names are bound at different times the actual polymorphic selection takes place at runtime in both cases, possibly through different but still equivalent mechanisms.
I totally ignore what's your claim here ... yes, the STL is a standard but it's also a library design, so what's your logical point ??
yes, but sice I want to avoid yet another flame war based on loose terminology and ambiguous problem statements, before explaning we should agree on what we're speaking about ... otherwise, it's just time wasted speaking of nothing. Anyway, your answer implies you don't agree on the problem formulation itself, so, I'm fine stopping here on this issue.
but what happens if a thread tries to delete something new-ed by another thread ? in the worst case, you'll get UB ( for example, this is the case on winapi heap functions with synchornization turned off ), at best you'll get a performance degradation due to the inevitable synchronization. Note that not only the standard allows memory de/allocations between different thread, it also promotes them ( see for example the new exception_ptr specification ).
You introduced STL as an example of good template based design. I say fine but since STL is a standard it's at most an example of a well designed programmer's interface. It doesn't tell much about the state of template designs in general. Even if you think Mr. Stepanov got STL right, concrete STL libraries may be complete disasters internally as template designs tend to be.
We don't need a problem formulation, we need you to finally explain what you mean. Otherwise there's not much to discuss is there.
The inner workings of the default allocator is an implementation detail. From the programmer's perspective it's business as usual. Multithreading must be properly synchronized. The only thing a programmer may notice (if one heap per thread is used rather than a global shared heap) is that allocations are faster.Quote:
but what happens if a thread tries to delete something new-ed by another thread ?
I agree since the standard specifies the interface, not the implementation, but that's the thing: when I think of "library design", I think of the interface provided by the library, in this case the parts of the standard library that come from the STL. When you wrote "template based designs", the thought that came to my mind was "the design of the interface", not "implementation". Hence, superbonzo's assertion that '"connected", "brittle" and "monolithic" are properties related to a (library) design, not its implementation' makes sense to me. Have you never heard of the design of library interfaces being described as such?Quote:
Originally Posted by nuzzle
And yet... The compiler cannot KNOW that memory allocated on one thread will never be deallicated in another thread. So the default implementation for any allocator must always assume synchronisation is needed. Only if you --the programmer-- know for sure that x-thread access to allocator isn't ever happening can you safely use an unsynced heap by explictely defining this in your code.
The one possible exception to this is if you link against a single threaded runtime lib that doesn't offer any kind of support for multithreading.
Most multithreaded apps don't need multiple heaps because even with synchronisation, the heap is rarely the bottleneck. And if it is... then separate heaps per thread (even if feasible) rarely help enough to make a real change to performance. If your bottleneck is the heap, it's more likely that fragmentation if your real culprit and multiple heaps won't really fix that. custom allocators can.
No, I've never really heard programmer interfaces to libraries being discussed in those terms. But I'm positive they can because after all they're designs too.
I think superbonzo threw STL into the discussion by mistake, and then just couldn't bring himself to admit STL in fact is a standard and has little to do with this discusson.
And I know why he did it. It's because the STL standard was designed by Mr. Stepanov who is also the father of the design methology superbonzo claimes is superior (or at least scales better) than object orientation.
This becomes pretty clear in superbonzo's post #25 where he defines "template based design" in terms verbatim from the book Elements of Programming by the very Mr. Stepanov (even though Mr. Stepanov never makes C++ templates the basis of his design methology).
So sure, there's tension between OO and the design methology of Mr. Stepanov (a high profile critic of OO). But for C++ it doesn't mean OO designs scale worse than template based designs. As I've argued, they scale approximately the same in practice.
Every C++ programmer should be aware of both design approaches and be able to mix and match. Programming still is an art.
laserlight, I think it's time to surrender to nuzzle's mind reading abilities ... now, I'm just curious to see if OReubens has more patience than I have ... :)
At the risk of sticking my neck out, I wonder if I could give my two cents on the issue of the usage pointers vs. references discussed back in posts 5 through 9 of this thread? (Please excuse me if this distract's too much from the way the thread has evolved since then.)
In my opinion, the problem with references is that they hide pointers, and thus obfuscate what is really going on in many cases.
For an example please see my comments on this very topic in a post last month: http://forums.codeguru.com/showthrea...t=#post2111485.
As I discussed there, references hide ownership issues. I meant to state (but as laserlight pointed out, failed to clearly do so) that they also hide when an object can be modified by a callee.
In the case of std::swap(a, b) you assume from the function name that objects and a and b will be modified, but what if the function name is not clear? What if you come across a line of code that says something like
?Code:bravo.CalculateAndStoreMetaData(a, b, c); // Example 1
In a system that is rampant with misused references, how can you tell from this line of code where the meta-data will get stored? Does it get stored in the bravo object or in a, b, or c?
If instead you see
Now you've got a clue that c might be an argument to an "out" parameter.Code:bravo.CalculateAndStoreMetaData(a, b, &c); // Exmaple 2
Really, the power of what I advocate comes from the combination of const-correctness, not using references, and Intellisense.
Using the above fictitious example, if a and b are large objects, it's nice to know whether they are being deep-copied (I usually work in time-critical code and thus usually care) but if a and b are arguments to reference parameters, then again that issue is obfuscated. Instead, I would much rather see
and thereby I'm far closer to knowing with a single glance what is really going on. If I also know the code is const-correct, I can then use Intellisense to see that a and b are arguments to pointers-to-const, but that c is an argument to a pointer-to-mutable. Now I know that the function verly likely either takes ownership of or modifies it's third argument.Code:bravo.CalculateAndStoreMetaData(&a, &b, &c); // Example 3
With Example 1, you can use Intellisense and const-correctness to figure out the modification/ownership issue, but you are required to use that second step, whereas instead with pointers it's often (in my experience) sufficient simply to know the objects aren't being deep-copied, but with only one glance you just can't get that information with obfuscators, er um, I meant to say references.
I do like references quite well in some specific uses (as I mentioned in my other post linked above). Otherwise, I thus far haven't seen that they are of any non-negligible benefit at all (did I miss some reason why I should be trying to make C++ look more like Visual Basic?) except for the parameter-can't-be-null issue (which in my opinion doesn't outweigh the above disadvantages most of the time) and for some specific uses in obtaining type-information for template classes.
Seems to me that you want to communicate too much with a pointer. It should communicate ownership and 'out' parameters, but it is also the only (descent) way to have optional function parameters. That means that whenever I see a pointer being used, I still don't know why it is used.
Or it might be an optional parameter. Why not just return the value of c?
Even if you have multiple return values, you can std::tie.
You can also use intellisense to see if a function takes a parameter by value, by const reference or by reference. You just argued against this.
Besides, I don't find "knowing that either ... or ..." a strong argument when talking about code clarity. You still don't know what's going on, so the code is still not clear.
I think the point is that you are trying to solve the wrong problems with references. You cannot convey ownership with references or pointers; use smart pointers instead, because they do convey ownership. Regarding out parameters, my advice would be to try to avoid them where possible. In the case of multiple return values, just return a tuple (or a dedicated struct/class that communicates the meaning of each return value).
no problem ( anyway, in the meantime those topics went in a dead end for some reason ... )
regarding the pointer VS reference issue, if one strictly restricts himself to C with classes then your arguments do make sense to me. But, when you start using STL-like libraries and things like smart pointers, etc... you'll end up using references anyway, so imposing a pointer-only coding style to your own code would result in making the code even more confusing, wouldn't it ?
in any case, as D_Drmmr pointed out, the benefits you mentioned fade away the higher level the code becomes, because in this case you'll need more complex ownership/value passing abstractions than a simple pointer can handle anyway, the net result being just polluting the code with "&*"'s everywhere ...
it is neither the purpose nor the intention of either pointers or references to "obfuscate".
Pointers have their uses, and so do references. Sometimes their uses are interchangable in which case going for the reference is better because it removes a level of indirection and thus provides code clarity.
Sometimes they aren't interchangable in which case the discussion is moot, use the one that needs to be used.
Pointers by themselves do not imply ownership of anything. Now if you're talking smart pointers, then yes, some of those have ownership semantics built in.Quote:
As I discussed there, references hide ownership issues.
Are you implying that if you create a dynamic array (let's take a std::vector), and you do something like: foo* pFoo = &fooVector[4]; that the pointer now ownes the element at index 4 while the vector owns the rest ? what do you expect to happen when the vector gets deleted ? Or are you implying that writing code like this is (or should be) invalid because it breaks your notion of ownership ?
The 'right to' modify is not an inherent trait of pointers or references.Quote:
I meant to state (but as laserlight pointed out, failed to clearly do so) that they also hide when an object can be modified by a callee.
if you want to convey that a parameter is not modified by the function, then that's what const is for.
if you want to more closely convey input, output or onput/output parameters, then this is information that currently is not part of the standard. There are however several attempts with various degrees of compiler assistance at solving this with annotation. MS has such an annotation throughtout (most of) the platform SDK
This is a shortsighted approach that may work for you, but wouldn't work as a general approach.Quote:
If instead you see
Now you've got a clue that c might be an argument to an "out" parameter.Code:bravo.CalculateAndStoreMetaData(a, b, &c); // Exmaple 2
Now what if the 3rd parameter was optional. So you make it a pointer so you can pass NULL to say that there is no object for that 3rd parameter... does this then imply it still has to be an out ? or could it be an in also (a lot of the Windows SDK uses this).
How would you convey a parameter that is both in and out ?
Reference parameters are never copied that's why they are references and not value parameters.Quote:
Using the above fictitious example, if a and b are large objects, it's nice to know whether they are being deep-copied (I usually work in time-critical code and thus usually care) but if a and b are arguments to reference parameters, then again that issue is obfuscated.
If you pass pointers... Then how (if at all) do you make sure that the user of the function knows that they aren't allowed to pass NULL ?
and in doing so you enforce your vision onto everyone else, and you now have 3 out parameters ? (they're all modifiable by your former definition), and parameters that could be passed as NULL.Quote:
Instead, I would much rather see
Code:bravo.CalculateAndStoreMetaData(&a, &b, &c); // Example 3
and this is an archaic way of thinking. You seem to want 'control' over what you think the compiler should be doing rather than telling the compiler what you want and letting the compiler figure out the most optimal code.Quote:
and thereby I'm far closer to knowing with a single glance what is really going on.
This same way of thinking is what makes people write weird convoluted code sequences because they think that will result in more optimal code. Optimizing compilers have evolved to the point where most of those tricks only make your code harder to read and maintain but don't give you better code (on the contrary, you might get worse code).
taking ownership... no. You have made it harder to understand because now that option (as well as NULL) could be potentially possible. So you have to know more about how the function works to be sure. This makes your solution "less oop".Quote:
Now I know that the function verly likely either takes ownership of or modifies it's third argument.
This implies you desire control at the caller side, which again goes against basic oop principles. The functions knows best if it requires deep copying or not, or it was coded by someone that doesn't understand what they're doing which is bad any way you look at it.Quote:
instead with pointers it's often (in my experience) sufficient simply to know the objects aren't being deep-copied
The caller really has no choice in this matter. If a function has a value parameter, then this means the creator of that function desired a deep copy, it's not the caller fault if this is the case, nor is there anything you can really do other than not calling the function which is likely not be an option.
You p.o.v. seems to imply the caller does have choice in things while they don't, so why do you make things harder by introducing pointers when what you really want is a reference ?
It didn't take any mind reading to see the obvious. What I find curious is why you didn't reveal your "source of inspiration" up front. After all Mr. Stepanov is an authority figure of STL fame. His methology is well established and has a substantial following and by many considered the major alternative to OO.
Sorry to see you regress when opposed with strong argumentation. When OReubens realizes he's wrong I hope at least he is upstanding enough to admit it.
I'm so sorry I must confess, sometimes I get really confused, sometimes I don't even remember if I read a book or not ( but luckily you know better than me ! ) ... then, dominated by embarassment I try to hide the truth foolishly hoping nobody will disclose me. So, now that I've been sunk by your powerful dialectics, please, be charitable and leave me alone in shame ... I really, really hope OReubens will be wise enough to retract before being too late, for his own good, for the good of all of us ... ... :):):) ... seriously, sometimes your provocative tone is really entertaining ... :D