Note that there are some explanatory texts on larger screens.

plurals
  1. POC++ Virtual Constructor, without clone()
    primarykey
    data
    text
    <p>I want to perform "deep copies" of an STL container of <strong>pointers to polymorphic classes</strong>.</p> <p>I know about the <em>Prototype</em> design pattern, implemented by means of the <em>Virtual Ctor Idiom</em>, as explained in the <a href="http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.8" rel="nofollow noreferrer">C++ FAQ Lite, Item 20.8</a>.<br> It is simple and straightforward:</p> <pre><code>struct ABC // Abstract Base Class { virtual ~ABC() {} virtual ABC * clone() = 0; }; struct D1 : public ABC { virtual D1 * clone() { return new D1( *this ); } // Covariant Return Type }; </code></pre> <p>A deep copy is then:</p> <pre><code>for( i = 0; i &lt; oldVector.size(); ++i ) newVector.push_back( oldVector[i]-&gt;clone() ); </code></pre> <h1>Drawbacks</h1> <p>As Andrei Alexandrescu <a href="http://www.informit.com/articles/article.aspx?p=31529&amp;seqNum=5" rel="nofollow noreferrer">states it</a>:</p> <blockquote> <p>The <code>clone()</code> implementation must follow the same pattern in all derived classes; in spite of its repetitive structure, there is no reasonable way to automate defining the <code>clone()</code> member function (beyond macros, that is).</p> </blockquote> <p>Moreover, clients of <code>ABC</code> can possibly do something bad. (I mean, nothing prevents clients to do something bad, so, it <em>will</em> happen.)</p> <h1>Better design?</h1> <p>My question is: is there another way to make an abstract base class clonable without requiring derived classes to write clone-related code? (Helper class? Templates?)</p> <hr> <p>Following is my context. Hopefully, it will help understanding my question.</p> <p>I am designing a class hierarchy to perform operations on a class <code>Image</code>:</p> <pre><code>struct ImgOp { virtual ~ImgOp() {} bool run( Image &amp; ) = 0; }; </code></pre> <p>Image operations are user-defined: clients of the class hierarchy will implement their own classes derived from <code>ImgOp</code>:</p> <pre><code>struct CheckImageSize : public ImgOp { std::size_t w, h; bool run( Image &amp;i ) { return w==i.width() &amp;&amp; h==i.height(); } }; struct CheckImageResolution { ... }; struct RotateImage { ... }; ... </code></pre> <p>Multiple operations can be performed sequentially on an image:</p> <pre><code>bool do_operations( vector&lt; ImgOp* &gt; v, Image &amp;i ) { for_each( v.begin(), v.end(), /* bind2nd( mem_fun( &amp;ImgOp::run ), i ... ) don't remember syntax */ ); } </code></pre> <p>If there are multiple images, the set can be split and shared over several threads. To ensure "thread-safety", each thread must have its <strong>own copy of all operation objects</strong> contained in <code>v</code> -- <code>v</code> becomes a prototype to be deep copied in each thread.</p> <p><strong>Edited:</strong> The thread-safe version uses the Prototype design pattern to enforce copy of <em>pointed-to-objects</em> -- not ptrs:</p> <pre><code>struct ImgOp { virtual ~ImgOp() {} bool run( Image &amp; ) = 0; virtual ImgOp * clone() = 0; // virtual ctor }; struct CheckImageSize : public ImgOp { /* no clone code */ }; struct CheckImageResolution : public ImgOp { /* no clone code */ }; struct RotateImage : public ImgOp { /* no clone code */ }; bool do_operations( vector&lt; ImgOp* &gt; v, Image &amp;i ) { // In another thread vector&lt; ImgOp* &gt; v2; transform( v.begin(), v.end(), // Copy pointed-to- back_inserter( v2 ), mem_fun( &amp;ImgOp::clone ) ); // objects for_each( v.begin(), v.end(), /* bind2nd( mem_fun( &amp;ImgOp::run ), i ... ) don't remember syntax */ ); } </code></pre> <p>This has sense when image operation classes are small: do not serialize accesses to unique instances of <code>ImgOp</code>s, rather provide each thread with their own copies.</p> <p>The hard part is to avoid writers of new <code>ImgOp</code>-derived classes to write any clone-related code. (Because this is implementation detail -- this is why I dismissed Paul's answers with the Curiously Recurring Pattern.)</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.
 

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