|
|||
|
Hi there,
I've been using smart pointer to save my efforts on dealing with memory management in C++. A sample of code is as below. //////// begin of code ///////// #define PtrOf(X) boost::shared_ptr < X > .... class Animal { public: Animal(const std::string& name) { _name = name; } private: std::string _name; }; .... PtrOf(Animal) animal(new Cow()); .... //////// end of code ///////// It has been worked well, however there are two ugly problems: 1) If we put the macro in a class interface, it's really ugly and obstruct the clarity of the interface. class Farm { public: Farm(PtrOf(Animal) a); }; 2. More seriously, if we have to use some 3rd party libraries which do not use the same convention, instead using bare pointer, we have a bigger problem to deal with. My question is whether there is a way to address C++ memory management to reduce it to a painless task ( as Java and C# ) without using 3rd party garbage collector ? Thanks, Ken -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|
||||
|
||||
|
|
|
|||
|
On Mar 19, 2:52 pm, MN <nguyent...@gmail.com> wrote:
> Hi there, > > I've been using smart pointer to save my efforts on dealing with > memory management in C++. A sample of code is as below. > > //////// begin of code ///////// > #define PtrOf(X) boost::shared_ptr < X > prefer typedef > ... > class Animal { > public: > Animal(const std::string& name) { _name = name; } > > private: > std::string _name;}; typedef boost::shared_ptr<Animal> PtrAnimal; > ... > PtrOf(Animal) animal(new Cow()); PtrAnimal animal(new Animal("Cow") ); > ... > //////// end of code ///////// > > It has been worked well, however there are two ugly problems: > > 1) If we put the macro in a class interface, it's really ugly and > obstruct the clarity of the interface. > > class Farm { > public: > Farm(PtrOf(Animal) a); becomes Farm(PtrAnimal a); > > }; > > 2. More seriously, if we have to use some 3rd party libraries which do > not use the same convention, instead using bare pointer, we have a > bigger problem to deal with. Use a wrapper to handle the gory details. void LibWrapper(PtrAnimal a) { libCall( a.get() ); } > > My question is whether there is a way to address C++ memory management > to reduce it to a painless task ( as Java and C# ) without using 3rd > party garbage collector ? > Sure. Just allocate memory an never worry about freeing it :^) OK that's not a good strategy. I don't know of a any easy way to do it. That's probably one major reason why garbage collection was added to Java and C#. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On 19 Mar, 20:52, MN <nguyent...@gmail.com> wrote:
> My question is whether there is a way to address C++ memory management > to reduce it to a painless task ( as Java and C# ) Memory management is certainly not painless in any language. In Java- like languages the pain is just shifted from coding phase to deployment/running phase (hint: search for "GC tuning" to get the idea). It is a different kind of pain. To stir the subject a bit it is worth to remember that not using dynamic memory *at all* is a valid approach to memory management and the difference between C++ and Java is that this approach is actually feasible in C++. No, it is not painless either, but it can be a very effective tool for some class of problems. In short: no silver bullets (surprise!). -- Maciej Sobczak * www.msobczak.com * www.inspirel.com The C++ Database Access Library: http://soci.sourceforge.net -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On Mar 19, 2:52 pm, MN <nguyent...@gmail.com> wrote:
> 1) If we put the macro in a class interface, it's really ugly and > obstruct the clarity of the interface. > > class Farm { > public: > Farm(PtrOf(Animal) a); Seems like the macro just saves a few characters of typing. I agree that is obstructs the clarity, mainly because it changes the angle brackets to parenthesis. One thing I like to do is define a type 'sp_t' in the class: class Animal { public: std::tr1::shared_ptr<Animal> sp_t; ... }; class Farm { public: Farm(Animal::sp_t animal); ... }; So the smart pointer type now only costs you 5 characters over an *. You could even make it shorter if you want because, unlike a macro, it lives within a narrow namespace. Another benefit is that you can modify Animal to use a different smart pointer type, say boost::intrusive_ptr, you only have to change one typedef. Drawbacks with this approach are: If you derive 'Pig' from 'Animal', you have to remember to re-declare sp_t or it will give you the inherited one. Another is that you will sometimes need to add the 'typename' keyword when using it inside templates. These haven't seemed to be big deal in practice. > 2. More seriously, if we have to use some 3rd party libraries which do > not use the same convention, instead using bare pointer, we have a > bigger problem to deal with. The problem is not that you're using shared_ptr when the library isn't, there's always a straightforward way to make that transition. The root cause is that you're formalizing the ownership of dynamically- allocated objects in source code, where previously you and the library authors did that it in ways the compiler couldn't help you with. Here the compiler is like your best friend who stands by you, helpfully pointing out all your mistakes before you make them, at the cost of his own popularity. > My question is whether there is a way to address C++ memory management > to reduce it to a painless task ( as Java and C# ) without using 3rd > party garbage collector ? The boost and std::tr1 smart pointers are just about the best thing since sliced bread in that department, IMHO. I have seen hundreds of thousands of lines of C++ put into 24/7 continuous production environments without ever having had a leak bug found thanks to smart pointers. That sounds like a silver-bullet-category solution to me. Yes, your code will look a little different, because it is different. In just a short amount of time, your code-reading eyes will come to see a smart pointer in a type signature as a reassuring, helpful thing. - Marsh -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On Mar 19, 12:52 pm, MN <nguyent...@gmail.com> wrote:
> Hi there, > > I've been using smart pointer to save my efforts on dealing with > memory management in C++. A sample of code is as below. > > //////// begin of code ///////// > #define PtrOf(X) boost::shared_ptr < X > > ... > class Animal { > public: > Animal(const std::string& name) { _name = name; } > > private: > std::string _name;}; > > ... > PtrOf(Animal) animal(new Cow()); > ... > //////// end of code ///////// > > It has been worked well, however there are two ugly problems: > > 1) If we put the macro in a class interface, it's really ugly and > obstruct the clarity of the interface. > > class Farm { > public: > Farm(PtrOf(Animal) a); > > }; Why do you need that macro at all? What does it save you? > 2. More seriously, if we have to use some 3rd party libraries which do > not use the same convention, instead using bare pointer, we have a > bigger problem to deal with. There's nothing you can do with that. If an existing library uses manual memory management, then it just does. You can still wrap raw pointers it returns into smart pointers on your side (assuming you own the pointer, of course), and, if the library provides custom deletion functions, you can use shared_ptr deleter facility to automatically invoke them. But that's about it. > My question is whether there is a way to address C++ memory management > to reduce it to a painless task ( as Java and C# ) without using 3rd > party garbage collector ? First of all, your #2 is equally applicable to either Java or C# - when using JNI or P/Invoke to deal with a third-party library that's written in a language with manual memory management that exposes raw pointers, you also have to deal with deallocation. In fact, the lack of smart pointers (or facilities to create them) in those languages makes the problem worse. Also, memory management (and, in general, resource management) is never a painless task, even when GC is involved. There is always some form of trade-off, and none of them allow for seamless memory use. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
Hi!
MN schrieb: > I've been using smart pointer to save my efforts on dealing with > memory management in C++. Sounds good. > //////// begin of code ///////// > #define PtrOf(X) boost::shared_ptr < X > This is really ugly in my opinion. Why not use "shared_ptr" directly? ToMchTypng? > 1) If we put the macro in a class interface, it's really ugly and > obstruct the clarity of the interface. > > class Farm { > public: > Farm(PtrOf(Animal) a); > }; Yes, that is why you can use "shared_ptr<Animal>" directly or a "typedef shared_ptr<Animal> AnimalPtr". > 2. More seriously, if we have to use some 3rd party libraries which do > not use the same convention, instead using bare pointer, we have a > bigger problem to deal with. Well, then do not think about memory management, but think of ownership. Of course you can use shared_ptr everywhere, but maybe a "Farm" always owns its animals. In this case the farm can use a pointer container internally (either deque<shared_ptr<Animal> > or ptr_deque<Animal>) and give away plain pointers or references (which makes interfacing with 3rd party easier). Frank -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
> 1) If we put the macro in a class interface, it's really ugly and
> obstruct the clarity of the interface. The macro is completely useless and ugly anway. It's useless because if you want to make your code shorter, you can use "using oost::shared_ptr" in the implementation files. And it's ugly because macros generally are. > class Farm { > public: > Farm(PtrOf(Animal) a); > }; > > 2. More seriously, if we have to use some 3rd party libraries which do > not use the same convention, instead using bare pointer, we have a > bigger problem to deal with. If you had ever bothered to read the documentation for shared_ptr, you would have seen that there is a "get" Method that returns a T*. > My question is whether there is a way to address C++ memory management > to reduce it to a painless task ( as Java and C# ) without using 3rd > party garbage collector ? There isn't, but most of the time shared_ptr and unique_ptr should get the job done. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
Hey, don't forget weak_ptr for circular-reference breakup! ;-)
IMO, there is no silver bullet©. Smart pointers help __a lot__ (but they are quite intrusive). Third-party GC helps, too, but, like with other GC-s, you may/will loose deterministic resource lifetime handling (RAII is big in C++ land ;-)). AFAIK, there's no generic solution for lifetime handling of third- party objects. We have to follow third-party conventions. Boost or whatever can't possibly know them. But we can be inspired by boost even there ;-). Other than that, IMO, what helps the most is a clean design with regard to heap object ownership. At every point in code, it should be clear to a programmer who is an owner of a pointer. The way I see it, there are these owners: 1. C++ runtime 1.1. stack objects 1.2. statics 2. Programmer (heap objects held through a raw pointer) 3. Another object (heap object that is a member of a class instance) 3.1. smart pointers are a most notable example 3.2. raw pointer members in a class Ownership transfer points in code should be minimized and clearly pointed out. To minimize possibility for errors, one should avoid pointer members that are not owned by a class instance. Whenever possible, a reference should be used as a parameter. Static objects should almost always be obtained through a reference. When using boost, perhaps we need shared/weak_ref? I imagine that it would wrap shared/weak Ptr and expose the following properties: couldn't emit a NULL pointer (e.g. couldn't be created of a NULL shared_ptr), would have operator T& and no get(). BTW, when using boost smart pointers, I usually typedef them like this: class MyClass; typedef boost::shared_ptr<MyClass> SPMyClass; typedef boost::weak_ptr<MyClass> WPMyClass; This goes into SPMyClass.h, not in MyClass.h. (I am in some huge codebases where changing a iota in a random header leads to 15-minutes recompile, so I've become paranoid about compile-time dependencies :- ( . To my defense, it's mostly not my fault ;-) HTH, Goran. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
MN wrote:
> I've been using smart pointer to save my efforts on dealing with > memory management in C++. A sample of code is as below. > > //////// begin of code ///////// > #define PtrOf(X) boost::shared_ptr < X > That is extremely ugly, because it hides the fact that PtrOf is a macro, with all the hairy little features that comes with that. > It has been worked well, however there are two ugly problems: > > 1) If we put the macro in a class interface, it's really ugly and > obstruct the clarity of the interface. Even worse, it obstructs what is essential to me when passing pointers: who owns the thing? If you use 'std::auto_ptr<T>', you know that ownership is transferred. If you use 'T const*', you can often assume that the ownership is retained and that the object is only passed for inspection. Further, the question is whether the pointer can be null. Actually, it is even hard to pass a null pointer, since you don't even know the pointer type and particularly smart pointers don't have implicit conversions. That requires use of 'PtrOf(T)()' which is even less readable. > 2. More seriously, if we have to use some 3rd party libraries which do > not use the same convention, instead using bare pointer, we have a > bigger problem to deal with. Well, if you need to pass pointers around to a third party library, you always have problems. In any case, there always is a way to extract a raw pointer from a smart pointer and unless ownership transfer takes place via raw pointers you at least have a chance to interact. > My question is whether there is a way to address C++ memory management > to reduce it to a painless task ( as Java and C# ) without using 3rd > party garbage collector ? No, I don't think so. However, I wonder why you object to using a good garbage collector but don't oppose to using a bad C++ library. I guess that is just internal politics, and for those there rarely is a technical solution. Uli -- Sator Laser GmbH Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932 [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
MN wrote:
> I've been using smart pointer to save my efforts on dealing with > memory management in C++. Good idea. > //////// begin of code ///////// > #define PtrOf(X) boost::shared_ptr < X > Not a good idea at all. The macro buys you nothing, except the chance of ruining unrelated code. Macros should be avoided wherever a better alternative exists, and if they are used, the should be in all-uppercase to avoid name clashes. > ... > class Animal { > public: > Animal(const std::string& name) { _name = name; } > > private: > std::string _name; > }; > ... > PtrOf(Animal) animal(new Cow()); > ... > //////// end of code ///////// > > It has been worked well, however there are two ugly problems: > > 1) If we put the macro in a class interface, it's really ugly and > obstruct the clarity of the interface. > > class Farm { > public: > Farm(PtrOf(Animal) a); > }; Forget about the macro. If you just want to shorten the type name, use a typedef: typedef boost::shared_ptr<Animal> AnimalPtr Then you can use: Farm(AnimalPtr a); > 2. More seriously, if we have to use some 3rd party libraries which do > not use the same convention, instead using bare pointer, we have a > bigger problem to deal with. There is no silver bullet against bad interfaces. When a third-party library insists on ownership transfer via raw pointers, you either have to live with it, or look for a better library. Note that you can still use smart pointers in your own code and release them only at the point where ownership passes to the library. Also note that the appearance of a raw pointer in an interface by itself does not mean that ownership is transferred. Consult the documentation to learn what the library actually does with the object to which the raw pointer points. > My question is whether there is a way to address C++ memory management > to reduce it to a painless task ( as Java and C# ) without using 3rd > party garbage collector ? 1. Avoid heap allocation where it is not necessary. 2. Use smart pointers where it is necessary. 3. Use diligence and caution where neither 1 nor 2 are an option. -- Gerhard Menzl Non-spammers may respond to my email address, which is composed of my full name, separated by a dot, followed by at, followed by "fwz", followed by a dot, followed by "aero". [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On Mar 19, 8:52 pm, MN <nguyent...@gmail.com> wrote:
> My question is whether there is a way to address C++ memory management > to reduce it to a painless task ( as Java and C# ) without using 3rd > party garbage collector ? If you're looking for a way to code as in Java and C#, then shared_ptr or similar potentially better things are indeed the solution. Be aware that the design of shared_ptr, while allowing easy integration with legacy code by taking pointers to already allocated memory, prevents optimality, even if using make_shared reduces the size overhead. shared_ptr also doesn't allow for cycles, but an implementation with that interface perfectly could. So there may be alternative designs more suitable for you or not. This is easy and trendy, but not recommended. While you seem to think GC, or GC-like behaviour, is a silver bullet for memory management, this is not true. The recommended way would rather be to avoid pointers or other structures with reference semantics, and use movable and/or copiable values with RAII instead. There have been numerous discussion on the topic, for example in the "We don't use C++ exceptions" thread (which is quite long, you'll need quite some courage to read through it) -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On Mar 19, 3:52 pm, MN <nguyent...@gmail.com> wrote:
> Hi there, > > I've been using smart pointer to save my efforts on dealing with > memory management in C++. A sample of code is as below. > It has been worked well, however there are two ugly problems: > > 1) If we put the macro in a class interface, it's really ugly and > obstruct the clarity of the interface. > > class Farm { > public: > Farm(PtrOf(Animal) a); > > }; The macro is not necessary for using smart pointers. Without much effort you can write a single typedef in a header for a type that is expected to be held in a smart pointer. For example, if we were writing your Animal class, you might find this in the header: class Animal; typedef boost::shared_ptr<Animal> Animal_shptr; class Animal { ... }; If you include the Animal header, the Animal_shptr typedef is available, and is self-documenting (provided you know what shptr stands for.) > 2. More seriously, if we have to use some 3rd party libraries which do > not use the same convention, instead using bare pointer, we have a > bigger problem to deal with. shared_ptr has "deleter" objects that you can register with the pointer when you create it. By default, when the thing needs to be destroyed it calls delete on the underlying pointer. But you can register a deleter that invokes some 3rd-party library API function to reclaim the memory (or does anything else, too, including a no-op), if that's what is required. Once the initial smart pointer is created with a deleter object, you pass it around as usual. The deleter object travels along with the pointer, so they remain together, even if the shared pointer type changes. (For example if a shared pointer to Object is given to a shared_ptr<void>, the proper deleter still works.) Chris -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On Mar 21, 2:51 am, Frank Birbacher <bloodymir.c...@gmx.net> wrote:
> Well, then do not think about memory management, but think of ownership. > Of course you can use shared_ptr everywhere, but maybe a "Farm" always > owns its animals. In this case the farm can use a pointer container > internally (either deque<shared_ptr<Animal> > or ptr_deque<Animal>) and > give away plain pointers or references (which makes interfacing with 3rd > party easier). { edits: removed quoted sig & banner. posters should do this themselves. tip: appropriate news clients do that automatically (google groups doesn't). -mod } As far as I know there are can be following relations between objects: 1. Association - and you will be forces to use smart pointers in any case 2. Aggregation - and you will have either the aggregated object or the pointer on it. In first case you wouldn't manage memory for aggregate. In second case you anyway should clean memory in destructor and implement modifier function for the aggregated object. 3. Using - when you receive pointer as parameter of some function. This case you are not responsible for the object's life circle. Any library you will use will do the same. As for me it is a good solution to use STL collections in case when you are not forced to use pointers as parameters. Or you can store objects that encapsulate pointers in collection because in STL collections addresses of elements can be changed by collection. Best wishes, Andrey -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
Thanks guys for your time and valuable comments. In summary, I found
the below good points: 1) Avoid macro, prefer typedef ( I changed the code ) 2) Use smart pointers whenever possible 3) And most important, clearly define the ownership of the pointer and make it visible when that ownership is transferred -- Ken -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On Mar 20, 9:01 pm, Mathias Gaunard <loufo...@gmail.com> wrote:
> shared_ptr also doesn't allow for cycles, but an implementation with > that interface perfectly could. Given that shared_ptr is required to be deterministic, how would it go about that without running a full circle-breaking GC cycle after every shared_ptr destructed (i.e. with reasonable performance)? -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|
![]() |
| Thread Tools | |
| Display Modes | |
|
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| Re: SAS Memory | Phil Rack | Newsgroup comp.soft-sys.sas | 0 | 03-19-2009 10:03 PM |
| Re: SAS Memory | Thomas Schmitt | Newsgroup comp.soft-sys.sas | 0 | 03-19-2009 09:46 PM |
| Re: SAS Memory | Sigurd Hermansen | Newsgroup comp.soft-sys.sas | 0 | 03-19-2009 07:06 PM |
| Re: z/OS memory questions | Charles Harbour | Newsgroup comp.soft-sys.sas | 1 | 11-03-2005 04:37 PM |
| Proc Report using RTF Out of Memory (8.1) on Mainframe | McDonald, John M | Newsgroup comp.soft-sys.sas | 0 | 08-06-2005 06:25 PM |