123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- namespace Lemma{
-
- /**
- \page Memory
-
- <div class="lemmamainmenu">
- \ref Intro "Intro"
- | \ref Compiling "Compiling"
- | \b Memory \b management
- | \ref Minimal "Minimal programme"
- | \ref EmSources "EM Sources"
- </div>
-
- \section MemoryManagement Memory Management
-
- \subsection Reference Reference counting and memory management
-
- \note Previous versions of Lemma used an internal reference counting mechanism, what
- is described below represents a significant change in the structure, philosophy,
- and internals of Lemma. To a large extent this has been possible due to changes
- in the C++ language specifications since C++-0x. These changes should improve the
- readability of Lemma code, decrease errors, improve performance, and reduce code length.
- The C++-17 standard promises to close a remaining oddities in the Lemma API, discussed below.
-
- Lemma relies heavily on smart pointer which utilize internal reference counting for several reasons:
- - It is not uncommon for multiple objects to have the same instantiations of an object as members.
- - Making copies of each object is not a viable option because the objects may be large, and they must all update simultaneously.
- - Without reference counting it is very easy to leave dangling pointers.
- - It is much easier to write code, as local copies of objects can be deleted
- as soon as they are no longer used within that context. The memory will not be
- freed and the object persists as long as other objects still reference it.
-
- The C++-0x standard introduced std::shared_ptr as a widely available, standard-compliant, high performance option to use
- in instances like this. The other smart pointer types specifically weak_ptr and unique_ptr are less handy in our application. As such,
- all Lemma classes expose a factory method returning a shared_ptr as the only publicly available means by which to construct objects.
- Internally, in cases where higher performance can be realized, manual memory management is employed.
-
- The base class for all Lemma objects is LemmaObject.
- The interface requires that all derived, non-abstract classes have <B>NewSP</B> and <B>DeSerialize</B> methods. These methods are the only
- mechanisms by which you should be creating Lemma objects.
-
- \code
- std::shared_ptr< DerivedClass > Object = DerivedClass::NewSP();
- // Or alternatively
- auto Object2 = DerivedClass::NewSP();
- \endcode
-
- \warning It is important to note that the default constructors and destructors are inaccessible so the following code is <B>NOT</B> valid.
- \code
- DerivedClass* Object = new DerivedClass;
- delete Object;
- \endcode
- Interestingly, the default constructors and destructors <B>are</B> public, however they are locked using a key struct
- <a href=http://stackoverflow.com/questions/8147027/how-do-i-call-stdmake-shared-on-a-class-with-only-protected-or-private-const/8147326#8147326> (link)</a>.
- The reason that this is necessary, is this arrangement allows for the use of std::make_shared to construct objects. The performance increase of this was
- chosen over the higher overhead associated with making the default methods protected.
-
- Many Lemma classes have other Lemma classes as data members. Classes may
- share these members, and depend on them being updated simultaneously. So
- that a situation like this is not unusual.
- \code
- DerivedClass* Component = DerivedClass::New();
- AnotherDerivedClass* Object2 = AnotherDerivedClass::New();
- YetAnotherDerivedClass* Object3 = YetAnotherDerivedClass::New();
- Object2->AttachMyComponent(Component);
- Object3->AttachMyComponent(Component);
- Component->Update(); // All three classes use the updated Component
- \endcode
- At this point both Object2 and Object3 have an up to date Component and any
- changes to Component can be seen by the Objects. Often these types of
- connections are happening behind the scenes and end users should not be
- troubled by any of this.
-
- Now in this example it is clear that if Component is deleted, than the objects
- will contain dangling pointers. To avert this, calling the
- ReferenceCountedObject::Delete() does not necessarily destroy the object.
- So that it would be safe -- continuing from the above example
- \code
- Component->Delete(); // The 'Component' handle will no longer be used.
- Object2->CallMethodRelyingOnComponent(); // But we can still use the object
- \endcode
-
- \subsection what Whats going on?
- Whenever we declared a new handle to the object-- either by calling New, or implicitly in the Connect methods--
- the true 'Component' object updated a list of pointers that had handles to it.
-
- \code
- DerivedClass* Component = DerivedClass::New(); // One Reference, 'Component'
- Object2->AttachMyComponent(Component); // Two References, internal in Object2
- Object3->AttachMyComponent(Component); // Three References, internal in Object3
- Component->Delete(); // Two References, 'Component' is gone.
- // DON'T USE Component->Method() ANYMORE!!!
- Object2->Delete(); // One Reference, Object2 releases handle upon Delete
- Object3->SomeFuntionReleasingComponent(); // Zero References, Component object is now deleted and
- // all memory is freed!
- \endcode
-
- \section So So what do I need to know?
- Not much. This is here to make life easy when doing high level programming. Just follow these simple rules
- - Allocate and free all variables with New and Delete, you have to do this as the default
- versions are protected.
-
- - Once calling Delete on an object it is no longer safe to use that handle. For example don't call
- Component->Delete(), and then call Component->Method(). Even if you 'know' that there are existing references
- and that it shouldn't have been deleted yet. Instead move your call to Delete down after your last direct use
- of each class.
-
- - Remember to call Delete, thanks to reference counting it is safe to do this as soon as you are done
- using an object even if other classes are using it. If you don't call Delete on objects you create, your
- program <B>will</B> leak memory!
-
- - Revel in the fact that memory is being managed for you, and that dangling pointers are not a concern.
-
- */
-
- }
|