Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Think <em>good and hard</em> about what you really want <code>MyClass</code> for. I've noticed that some programmers write wrappers for their collections just as a matter of habit, regardless of whether they have any specific needs above and beyond those met by the standard STL collections. If that's your situation, then <code>typedef std::list&lt;MyContainedClass&gt; MyClass</code> and be done with it.</p> <p>If you <em>do</em> have operations you intend to implement in <code>MyClass</code>, then the success of your encapsulation will depend more on the interface you provide for them than on how you provide access to the underlying list. </p> <p>No offense meant, but... With the limited information you've provided, it <em>smells</em> like you're punting: exposing internal data because you can't figure out how to implement the operations your client code requires in <code>MyClass</code>... or possibly, because you don't even <em>know</em> yet what operations will be required by your client code. This is a classic problem with trying to write low-level code before the high-level code that requires it; you know what data you'll be working with, but haven't really nailed down exactly what you'll be doing with it yet, so you write a class structure that exposes the raw data all the way to the top. You'd do well to re-think your strategy here.</p> <hr> <p><a href="https://stackoverflow.com/questions/108389/is-it-a-good-correct-way-to-encapsulate-a-collection#113052">@cos</a>: </p> <blockquote> <p>Of course I'm encapsulating MyContainedClass not just for the sake of encapsulation. Let's take more specific example:</p> </blockquote> <p>Your example does little to allay my fear that you are writing your containers before you know what they'll be used for. Your example container wrapper - <code>Document</code> - has a total of three methods: <code>NewParagraph()</code>, <code>DeleteParagraph()</code>, and <code>GetParagraph()</code>, all of which operate on the contained collection (<code>std::list</code>), <em>and all of which</em> closely mirror operations that <code>std::list</code> provides "out of the box". <code>Document</code> encapsulates std::list in the sense that clients need not be aware of its use in the implementation... but realistically, it is little more than a facade - since you are providing clients raw pointers to the objects stored in the list, the client is still tied implicitly to the implementation.</p> <blockquote> <p>If we put objects (not pointers) to container they will be destroyed automatically (which is good).</p> </blockquote> <p>Good or bad depends on the needs of your system. What this implementation means is simple: the document owns the <code>Paragraph</code>s, and when a <code>Paragraph</code> is removed from the document any pointers to it immediately become invalid. Which means you must be <em>very</em> careful when implementing something like:</p> <blockquote> <p>other objects than use collections of paragraphs, but don't own them.</p> </blockquote> <p>Now you have a problem. Your object, <code>ParagraphSelectionDialog</code>, has a list of pointers to <code>Paragraph</code> objects owned by the <code>Document</code>. If you are not careful to coordinate these two objects, the <code>Document</code> - or another client by way of the <code>Document</code> - could invalidate some or all of the pointers held by an instance of <code>ParagraphSelectionDialog</code>! There's no easy way to catch this - a pointer to a valid <code>Paragraph</code> looks the same as a pointer to a deallocated <code>Paragraph</code>, and may even end up pointing to a valid - but different - <code>Paragraph</code> instance! Since clients are allowed, and even expected, to retain and dereference these pointers, the <code>Document</code> loses control over them as soon as they are returned from a public method, even while it retains ownership of the <code>Paragraph</code> objects.</p> <p>This... is bad. You've end up with an incomplete, superficial, encapsulation, a leaky abstraction, and in some ways it is worse than having no abstraction at all. Because you hide the implementation, your clients have no idea of the lifetime of the objects pointed to by your interface. You would probably get lucky most of the time, since most <code>std::list</code> operations do not invalidate references to items they don't modify. And all would be well... until the wrong <code>Paragraph</code> gets deleted, and you find yourself stuck with the task of tracing through the callstack looking for the client that kept that pointer around a little bit too long. </p> <p>The fix is simple enough: return values or objects that can be stored for as long as they need to be, and verified prior to use. That could be something as simple as an ordinal or ID value that must be passed to the <code>Document</code> in exchange for a usable reference, or as complex as a reference-counted smart pointer or weak pointer... it really depends on the specific needs of your clients. Spec out the client code first, then write your <code>Document</code> to serve.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload