Matrix operations

Discussion in 'C++' started by et al., Jul 23, 2010.

  1. et al.

    et al. Guest

    Hi! I am learning C++, and I am trying to understand how operators
    work, and should work. So, I've decided to do yet another matrix class!
    Anyway, I'm stuck with deciding how I should implement things here,
    mainly regarding operator overloading.

    What I've done so far is this:

    class cmatrix
    {
    public:
    explicit cmatrix(unsigned int rows, unsigned int cols);
    explicit cmatrix(cmatrix& src);
    virtual ~cmatrix();
    virtual double& at(unsigned int r, unsigned int c);
    virtual cmatrix& operator=(const cmatrix src);
    private:
    vector<unsigned int, double> storage;
    };

    and I've successfully implemented the operator= (or better: it works),
    but now I've doubts with other operations! Before that, is my copy
    constructor well defined? And a silly question... does the operator=
    need a reference, or is it ok to leave it as I wrote?

    My question is easy: how do I multiply two matrices? I mean, in the
    "definition" sense, this is not an algorithmic doubt! Let's suppose I
    want to write this:

    M = N * Q;

    with M, N, and Q being cmatrix instances. That is the equivalent,
    according to Stroustrup's book, of

    M.operator=(N.operator*(Q));

    The problem is... How should I declare the operator* ? I don't want to
    modify N, and I'd like to avoid pointers! Forgive this silly question,
    but I'm still learning! :)

    Another problem is with cvector, derived from cmatrix. Will the same
    operator work? Like:

    v = M * w;

    or worse, what if I want to overwrite a cvector or cmatrix?

    v = M * v;


    Thanks for any help you can give me!
    et al., Jul 23, 2010
    #1
    1. Advertising

  2. On 7/23/2010 8:45 AM, et al. wrote:
    > Hi! I am learning C++, and I am trying to understand how operators work,
    > and should work. So, I've decided to do yet another matrix class!
    > Anyway, I'm stuck with deciding how I should implement things here,
    > mainly regarding operator overloading.
    >
    > What I've done so far is this:
    >
    > class cmatrix
    > {
    > public:
    > explicit cmatrix(unsigned int rows, unsigned int cols);
    > explicit cmatrix(cmatrix& src);


    A copy c-tor does not have to be explicit. I am not even sure what it
    means for a copy c-tor to be explicit. Also, the *usual* copy c-tor has
    the reference to const as its argument:

    cmatrix(cmatrix const& src);

    (also why did you call it "cmatrix", why not just "matrix"? any
    relation to the C language?)

    > virtual ~cmatrix();
    > virtual double& at(unsigned int r, unsigned int c);
    > virtual cmatrix& operator=(const cmatrix src);


    Better to pass the argument by reference, and why is it virtual?

    cmatrix& operator=(cmatrix const& src);

    > private:
    > vector<unsigned int, double> storage;
    > };
    >
    > and I've successfully implemented the operator= (or better: it works),
    > but now I've doubts with other operations! Before that, is my copy
    > constructor well defined? And a silly question... does the operator=
    > need a reference, or is it ok to leave it as I wrote?


    See above.

    > My question is easy: how do I multiply two matrices? I mean, in the
    > "definition" sense, this is not an algorithmic doubt! Let's suppose I
    > want to write this:
    >
    > M = N * Q;
    >
    > with M, N, and Q being cmatrix instances. That is the equivalent,
    > according to Stroustrup's book, of
    >
    > M.operator=(N.operator*(Q));
    >
    > The problem is... How should I declare the operator* ? I don't want to
    > modify N, and I'd like to avoid pointers! Forgive this silly question,
    > but I'm still learning! :)


    You're doing fine. If you decide to make the multiplication operator a
    member, then you do

    matrix operator*(const matrix& other) const;

    since it doesn't modify either operand. You could also define it a
    non-member, and make it a friend:

    friend matrix operator*(const matrix& L, const matrix& R);

    Notice that the arithmetic operand will create an object and return it
    by value.

    >
    > Another problem is with cvector, derived from cmatrix. Will the same
    > operator work? Like:
    >
    > v = M * w;


    Not sure what you mean. What's a cvector?

    > or worse, what if I want to overwrite a cvector or cmatrix?
    >
    > v = M * v;


    That should not be a problem if you define the function correctly.
    Return a value from it. The call to operator= will copy the returned
    object into the second (right-hand side) operand *after* the value of
    that operand were used to calculate the return value of the operator*.

    > Thanks for any help you can give me!


    You're welcome, and ask more!

    V
    --
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Jul 23, 2010
    #2
    1. Advertising

  3. et al.

    Jonathan Lee Guest

    On Jul 23, 8:45 am, et al. <> wrote:
    > Anyway, I'm stuck with deciding how I should implement things here,
    > mainly regarding operator overloading.


    Some general advice first: google for "operator overloading"
    and check out the Parashift link that comes up, and probably
    wikibooks. Also, there have been a number of threads in this
    newsgroup about good books to start out with. I'd probably
    look for that, too.

    <snip>

    > Before that, is my copy constructor well defined?


    In C++ speak you haven't defined it, here. Just declared
    it. Anyway, the usual declaration would be

    cmatrix(const cmatrix&);

    i.e., drop the explicit and add const. The const will
    allow you to copy from a wider range of sources. The
    explicit is probably not necessary unless you have some
    specific reason to do so.

    > And a silly question... does the operator=
    > need a reference, or is it ok to leave it as I wrote?


    It doesn't, but it's (more?) common to write it as
    accepting a reference. If you're going to pass by value
    as you've done, you don't need const. The argument is
    your own private copy that exists for the function call
    only. You can do what you want to it.

    > The problem is... How should I declare the operator* ?


    Inside the class:

    cmatrix operator*(const cmatrix& Q) const;

    Or you have the option of doing a global overload outside
    the class

    cmatrix operator*(const cmatrix& N, const cmatrix& Q);

    Quite often these operators call their assignment
    equivalents. For example

    cmatrix operator*(const cmatrix& N, const cmatrix& Q) {
    cmatrix M(N); // make a copy
    M *= Q; // call the mul-and-assign operator
    return M;
    }

    > Another problem is with cvector, derived from cmatrix. Will the same
    > operator work? Like:
    >
    > v = M * w;


    You're going to need to write a separate assignment operator,
    but sure.

    > or worse, what if I want to overwrite a cvector or cmatrix?
    >
    > v = M * v;


    The result of M * v is a temporary, which is then used for
    the assignment. The temp doesn't get a name, but it's a
    distinct object from v and so "overwriting" does not occur.

    Note that you *can* overwrite in this sense

    v = v;

    But usually in operator=(...) you just check that the source
    is not the same as the *this object before making a useless
    copy.

    --Jonathan
    Jonathan Lee, Jul 23, 2010
    #3
  4. et al.

    James Kanze Guest

    On Jul 23, 3:18 pm, Jonathan Lee <> wrote:
    > On Jul 23, 8:45 am, et al. <> wrote:


    [...]
    > > Before that, is my copy constructor well defined?


    > In C++ speak you haven't defined it, here. Just declared
    > it. Anyway, the usual declaration would be


    > cmatrix(const cmatrix&);


    > i.e., drop the explicit and add const. The const will
    > allow you to copy from a wider range of sources. The
    > explicit is probably not necessary unless you have some
    > specific reason to do so.


    If he uses the classical patterns, with std::vector for the
    data, and a standard integral types for the dimensions, he
    doesn't necessarily have to declare a copy constructor; the one
    the compiler provides will have the correct semantics. (He may
    still want to define one, in order to be able to instrument it,
    or simply to prevent it from being inline.)

    [...]
    > > The problem is... How should I declare the operator* ?


    > Inside the class:


    > cmatrix operator*(const cmatrix& Q) const;


    > Or you have the option of doing a global overload outside
    > the class


    > cmatrix operator*(const cmatrix& N, const cmatrix& Q);


    A production level Matrix class would almost certainly not
    return a Matrix, but some sort of proxy, for performance
    reasons.

    For learning, however, this is the first pattern you should
    learn, and it works well for many, many types.

    > Quite often these operators call their assignment
    > equivalents. For example


    > cmatrix operator*(const cmatrix& N, const cmatrix& Q) {
    > cmatrix M(N); // make a copy
    > M *= Q; // call the mul-and-assign operator
    > return M;
    > }


    If this pattern is being used, then typically, you don't write
    the operator* at all, but rather derive, e.g.:

    class Matrix : public ArithmeticOperators<Matrix>
    {
    // ...
    };

    The template class ArithmeticOperators takes care of all of the
    binary operators, provided you define the <op>= function (which
    traditionally will be a member, although there are good
    arguments that this should not be the case).

    > > Another problem is with cvector, derived from cmatrix. Will
    > > the same operator work? Like:


    > > v = M * w;


    > You're going to need to write a separate assignment operator,
    > but sure.


    Without seeing exactly what he means, it's hard to say. I don't
    quite see how Vector could derive from Matrix.

    > > or worse, what if I want to overwrite a cvector or cmatrix?


    > > v = M * v;


    > The result of M * v is a temporary, which is then used for
    > the assignment.


    Which is why operator* shouldn't return a Matrix in production
    code.

    > The temp doesn't get a name, but it's a distinct object from
    > v and so "overwriting" does not occur.


    > Note that you *can* overwrite in this sense


    > v = v;


    > But usually in operator=(...) you just check that the source
    > is not the same as the *this object before making a useless
    > copy.


    Given the frequency of self-assignment, adding the check may
    globally slow things down: you save a lot of time in
    self-assignment, but you slow every assignment down by the time
    it takes to make the check.

    --
    James Kanze
    James Kanze, Jul 23, 2010
    #4
  5. et al.

    Jonathan Lee Guest

    On Jul 23, 12:59 pm, James Kanze <> wrote:
    > On Jul 23, 3:18 pm, Jonathan Lee <> wrote:
    > > Quite often these operators call their assignment
    > > equivalents. For example

    >
    > If this pattern is being used, then typically, you don't write
    > the operator* at all, but rather derive, e.g.:
    >
    >     class Matrix : public ArithmeticOperators<Matrix>
    >     {
    >         //  ...
    >     };


    Where is this class from? Or is it just something of your
    invention? In any case, I guess it's an interesting idea.
    But since the OP is asking about how to do operator
    overloading, I would stand by my recommendation to do them
    "manually".

    And really.. how often do you define a new arithmetic
    type?

    > Without seeing exactly what he means, it's hard to say.  I don't
    > quite see how Vector could derive from Matrix.


    As a 1-column or 1-row Matrix, I expect. Mathematically, there
    isn't much distinction between a matrix and a vector as a special
    case of a matrix. In particular, if he's doing any linear algebra,
    setting things up this way seems reasonable.

    > Given the frequency of self-assignment, adding the check may
    > globally slow things down: you save a lot of time in
    > self-assignment, but you slow every assignment down by the time
    > it takes to make the check.


    You're right, of course, but it also seems like a micro-optimization.
    I use this all the time and neither gprof nor Valgrind has ever
    scolded me for it.

    --Jonathan
    Jonathan Lee, Jul 23, 2010
    #5
  6. et al.

    James Kanze Guest

    On Jul 23, 6:20 pm, Jonathan Lee <> wrote:
    > On Jul 23, 12:59 pm, James Kanze <> wrote:


    > > On Jul 23, 3:18 pm, Jonathan Lee <> wrote:
    > > > Quite often these operators call their assignment
    > > > equivalents. For example


    > > If this pattern is being used, then typically, you don't write
    > > the operator* at all, but rather derive, e.g.:


    > > class Matrix : public ArithmeticOperators<Matrix>
    > > {
    > > // ...
    > > };


    > Where is this class from? Or is it just something of your
    > invention?


    This particular version is my own, but I imagine all experienced
    C++ programmers have something similar in their tool bag.

    > In any case, I guess it's an interesting idea.
    > But since the OP is asking about how to do operator
    > overloading, I would stand by my recommendation to do them
    > "manually".


    Certainly. I was commenting on your proposal as to the pattern.
    If you use the pattern of defining the <op>= operator first
    (which you almost certainly wouldn't in a production matrix
    class---this is an exercise), then you'd probably want to use
    such a base class for the <op> operators. It's worth
    mentionning the idiom, at the least, even if the poster needs to
    know how to write the <op> operator without the base class
    template, if only in order to be able to implement the base
    class template.

    (This is different than the question of how to write a
    production quality matrix class---writing a production quality
    matrix class needs mastery of a number of concepts which the
    poster probably hasn't seen yet.)

    > And really.. how often do you define a new arithmetic
    > type?


    It depends on the application domain; there's quite a few in my
    current work. But even if you don't define new arithmetic
    types, it's not rare to define a type which supports some
    arithmetic operators.

    > > Without seeing exactly what he means, it's hard to say. I don't
    > > quite see how Vector could derive from Matrix.


    > As a 1-column or 1-row Matrix, I expect.


    Which leads to the classical "is a cercle an ellipse" problem?
    A vector has an additional invariant, compared to matrix.

    > Mathematically, there isn't much distinction between a matrix
    > and a vector as a special case of a matrix.


    Yes, but the mathematical relationships don't always model by
    inheritance---again, consider the "is a cercle an ellipse"
    problem.

    > In particular, if he's doing any linear algebra,
    > setting things up this way seems reasonable.


    > > Given the frequency of self-assignment, adding the check may
    > > globally slow things down: you save a lot of time in
    > > self-assignment, but you slow every assignment down by the time
    > > it takes to make the check.


    > You're right, of course, but it also seems like a micro-optimization.
    > I use this all the time and neither gprof nor Valgrind has ever
    > scolded me for it.


    It certainly doesn't make the program incorrect, if it were
    otherwise correct. Practically speaking (and matrix may be an
    exception to this general rule), if you think you need a check
    for self alignment, the code is probably incorrect.

    --
    James Kanze
    James Kanze, Jul 25, 2010
    #6
  7. et al.

    Jonathan Lee Guest

    On Jul 25, 7:06 am, James Kanze <> wrote:
    > It's worth mentionning the idiom, at the least, even if
    > the poster needs to know how to write the <op> operator
    > without the base class template, if only in order to be
    > able to implement the base class template.


    That's true.

    > > You're right, of course, but it also seems like a micro-optimization.
    > > I use this all the time and neither gprof nor Valgrind has ever
    > > scolded me for it.

    >
    > It certainly doesn't make the program incorrect, if it were
    > otherwise correct.  Practically speaking (and matrix may be an
    > exception to this general rule), if you think you need a check
    > for self alignment, the code is probably incorrect.


    No, of course it it doesn't make the code incorrect. I only mean
    to comment on the way you presented it as an optimization. Sure,
    it's an optimization. But it's one that is wayyyyyy down the list
    of things I'll optimize.

    On the other hand, to the OP (if you're still reading): you will
    probably want to look up what self reference is, and how to avoid
    the troubles it can cause. James is right about not having to
    punish yourself with an explicit check.

    --Jonathan
    Jonathan Lee, Jul 25, 2010
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Jesus M. Salvo Jr.
    Replies:
    2
    Views:
    4,047
    robert
    Feb 11, 2006
  2. M. Fernandez

    Java Matrix Operations

    M. Fernandez, Mar 2, 2006, in forum: Java
    Replies:
    5
    Views:
    1,043
    ~Glynne
    Mar 17, 2006
  3. friends

    Sparse Matrix operations

    friends, Sep 17, 2005, in forum: C Programming
    Replies:
    0
    Views:
    493
    friends
    Sep 17, 2005
  4. Terry Reedy
    Replies:
    0
    Views:
    544
    Terry Reedy
    Apr 2, 2009
  5. Robert Kern
    Replies:
    0
    Views:
    584
    Robert Kern
    Apr 2, 2009
Loading...

Share This Page