Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>The main problem to watch out with inheritance-based designs like this is SLICING.</p> <p>Let's say MatrixBase defines a non-virtual assignment operator. It copies all the data members common to all matrix subclasses. Your SparseMatrix class defines additional data members. Now what happens when we write this?</p> <pre><code>SparseMatrix sm(...); MatrixBase&amp; bm = sm; bm = some_dense_matrix; </code></pre> <p>This code makes little sense (trying to assign a DenseMatrix to a SparseMatrix directly through an operator defined in the base class) and is prone to all kinds of nasty slicing behavior, yet is a vulnerable aspect of such code and there's a very big possibility of this happening somewhere down the line if you provide assignment operators accesible through MatrixBase*/MatrixBase&amp;. Even when we have this:</p> <pre><code>SparseMatrix sm(...); MatrixBase&amp; bm = sm; bm = some_other_sparase_matrix; </code></pre> <p>... we still have slicing issues due to the assignment operator being non-virtual. Without the inheritance of a common base class, we can provide assignment operators to meaningfully copy a dense matrix to a sparse one, but trying to do this through a common base class is prone to all kinds of issues.</p> <p><em>Assignment operators should generally be avoided for base classes!</em> Imagine a case where Dog and Cat inherit from Mammal and Mammal provided an assignment operator, virtual or not. It would imply that we can assign Dogs to Cats, which makes no sense and even if the operator were virtual, it would be difficult to provide any kind of meaningful behavior to assign mammals to other mammals. </p> <p>Let's say we try to improve the situation by implementing the assignment operator in Dog so that it can only be assigned other dogs. Now what happens when we inherit from Dog to create Chihuahua and Doberman? We should not be able to assign Chihuahuas to Dobermans, and so the original case recursively repeats itself until you're sure you've arrived at the leaf nodes of an inheritance hierarchy (it's a shame C++ does not have a final keyword to prevent any further inheritance).</p> <p>The same problem is apparent with the common Circle inherits Ellipse example. The circle might require width and height to match: that's an invariant the class wants to maintain, yet anyone can simply get a base pointer (Ellipse*) pointing to a Circle object and violate that rule. </p> <p>If in doubt, avoid inheritance as that is one of the most misused features of C++ and any language which supports object-oriented programming in general. You can try to work around the problem by providing runtime mechanisms to determine the type of a subclass being assigned to another subclass and only allow for matching types, but now you're doing a whole lot of extra work and incurring runtime overhead. It's better to avoid assignment operators all together for inheritance hierarchies and rely on methods like clone to produce copies (prototype pattern).</p> <p>Thus, if you choose to make an inheritance hierarchy out of your matrix classes, you should think carefully as to whether the (most likely short-term) advantages of inheriting outweigh the long-term disadvantages. You should also be sure to avoid all potential cases where slicing can occur, which might be very difficult to do for a matrix library without compromising its usability and efficiency.</p>
 

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