3

I am currently writing my first bigger project in c++. Its basic linear algebra. I am aware that boost libraries and others exist, but for various reasons I need to write my own. Purpose of the library is to perform certain vector operations (adding, substraction, etc. ...) for statistical data analysation. So ease of use of the functions has to be taken into consideration.

I started to write a class:

typedef std::vector<double> vec;
typedef std::vector<std::vector<double> > mat;

class Vector{
public: 
    Vector(){} 
    Vector(vec){}
    ~Vector(){}

    //functions to act on the vectors
    vec add(double){...}
    //many more... 

private:
    vec v
}; 

I soon realised that for simple vector arithmetic the class had some inconveniences: the missing assignment operators came to haunt me rather quickly.

Since I do not have long term coding experiences I want to know whether a "simple" file that contains all the necessary functions like:

 from Vector.h:

 typedef std::vector<double> vec;
 typedef std::vector<std::vector<double> > mat;

 vec add(vec v, double d){...};
 ...

is a proper solution for this kind of problem or if it is preferable to consider the class solution?

  • 1
    One of the big advantages to using a class is IntelliSense. A user can simply start typing myVec. to see what functions are available. With just a file they have to look in the documentation each time. Classes also allow for extension into specific types of vectors via inheritance and automatically called destructors. The myVec. notation is also likely to be more expected that func(myVec, ...); to most programmers because programmers usually expect classes for this sort of thing. So I think classes are the way to go here. P.S. This should really be on Code Review since your code works. – niemiro Sep 14 '14 at 7:30
  • 2
    @niemiro Most Java programers perhaps. In C++ you don't expect a class where it makes no sense. – juanchopanza Sep 14 '14 at 7:32
  • 2
    @juanchopanza: fair enough, thanks for the correction. To be honest, I probably shouldn't have answered since I'm only a hobbyist programmer who perhaps unwisely learned C++ and C# together and at the same time. Having never had a formal education in programming, or had a programming job, sometimes the conventions and methods I think I've learned probably often aren't quite as I think them to be. Thank you for the correction though, for both me and the OP. – niemiro Sep 14 '14 at 7:40
  • You really should want to code in C++11 and should not use names (like Vector) similar to standard names. Several features of C++11 will help you a big lot. – Basile Starynkevitch Sep 14 '14 at 7:42
  • I didn't understand what you were missing exactly with the class implementation. – StoryTeller Sep 14 '14 at 8:25
12

When you want to represent distinct concepts, you should create separate types. Yes, this may come with some boilerplate for operator overloading, but having distinct types may offer significant advantages down the line.

std::vector is a generic dynamic array, not a mathematical vector (despite borrowing its name). So yes, you should create a separate type.

Whether the operations you want are then implemented as members or as free functions is a separate design decision. I recommend reading Effective C++.

  • Hi Sebastian. Thx for your reply. As a matter of fact I am just writing the overloaded constructors for the vector class. Obviously I need the practice anyway so I can just start at right away. Second I think its "neeter" having all methods contained in a particular environment so it just seems like the better long term solution. However, there will be some free floating vector creation routines, for which I don't see any place in the class. – Vincent Sep 14 '14 at 11:13
  • 1
    Ironically, a mathematical vector would best be implemented by means of a std::array instead of a std::vector since the number of dimensions is fixed, i.e. the number of entries should be static (std::array) instead of dynamic (std::vector). – Arne Mertz Sep 18 '14 at 12:00
2

Your best bet would be to do operator overloads.

vec operator+(const vec& rhs) const

is what you'd use for a declaration, this page covers it in quite some detail: http://en.wikibooks.org/wiki/C%2B%2B_Programming/Operators/Operator_Overloading

Then you could just say:

vec a;
vec b;
vec c;

// Do stuff to initialize a and b

c = a + b;
1

One advantage of a free function in your particular case is that you can implement both addition of vectors and doubles

vec add(vec v, double d);

and vice versa

vec add(double d, vec v);

This is not possible with member functions.

Member functions, however, have the benefit of being able to access and even modify objects' internal states, which may be useful (generally, this is the reason for writing functors instead of functions). But I suppose in your case this is neither required nor even needed.

As for whether to write a wrapper class or not, it's a separate question. It depends on whether your vector needs to have an additional state or member functions that you want to be called with member function syntax (e.g. v.sort()). Sometimes it's just more elegant to write sort(v).

There're also a couple of other issues with your code. First, I don't see any reason for passing vectors around by values. Use const references instead. Second, the vector algebra is the classic example of where using operator overloading suggests itself.

Oh, and don't forget to write the += operator (it's convenient and may be implemented more efficiently).

  • Thank you for your reply. There is usual a tradeoff between different solutions for a problem. The overloaded operators will certainly be implemented at some point. Before doing that however, I wanted to know about the structure of the program. A come from a statistics background where "free" functions are the standard so I am biased towards that. Hence my question. Thank you again. – Vincent Sep 14 '14 at 7:57
  • My pleasure :-) – shakurov Sep 14 '14 at 8:01
1

The concept behind std::vectoris not the one of a mathematical vector, but the one of a dynamically sizable array.

Adding arithmetic operation to it, makes those operation available to whatever code in your development may use std::vector to do other things than linear algebra operations.

For this reason, if you need a mathematical vector, you had better to define a new independent type.

Of course nothing can stop you from using std::vector inside it to hold values.

Whether if operations should be members or free function applied to that type ... it is more a matter of taste than anything: a.add(b) and add(a,b) have exactly the same semantic power. I personally prefer the second, since it puts both a and b at the same level, or even operator+(a,b) (so that you can write a+b)

When implementing matrix, however, think a while before implementing them as vector of vectors for various reasons:

  • if vectors are implemented a "dynamically sizable" a vetor of vector will have all rows independently sizable and stored each other separately with all the structure (usually three pointers) to track them. For small matrices an vectors (like 4 components 3d-projective space) its more overhed than data.
  • A dynamic vector of dynamic vectors privileges the access by rows (or by columns) , but a matrix must be able to invert their role easily (thing to the Cij = Aik*Bkj matrix product)
  • Matricies are themselves "vector spaces" against + - and scalar * and /.

It is much better to think to matrices as independent types, implemented a s a plain vector, externally accessed with two indices, but whose basic operations are implemented in term of their inner vector.

I will probably come to something like this:

template<class T>
class vect
{
    std::vector<T> m;
public:    
    explcit vect(size_t n) :m(n) {}
    size_t size() const { return n; }
    T& operator[] (size_t i) { return m(i); }
    const T& operator[] (size_t i) const { return m[i]; }
};

template<class T>
vect<T> operator+(const vect<T>& a, const vect<T>& b)
{ 
    vect<T> z(a.size()); 
    for(size_t i=0; i<a.size(); ++i) 
        z[i] = a[i]+b[i]; 
    return z; 
}


template<class T>
class matrix
{
    vect<T> m;
    size_t R;
public:
    matrix(size_t r, size_t c) :m(r*c), R(r) {}
    matrix(const vect<T>& s, size_t r) :m(s), R(r) {}

    const vect<T>& as_vect() const { return m; }
    size_t rows() const { return R; }
    size_t cols() const { return m.size()/R; }

    T& operator()(size_t r, size_t c) { return m[r*cols()+c]; }
    const T& operator()(size_t r, size_t c) const { return m[r*cols()+c]; }
};


template<class T>
matrix<T> operator+(const matrix<T>& a, const matrix<T>& b)
{ return matrix<T>(a.as_vect()+b.as_vect(),a.rows()); }

The same can be done for both matrix and vect about operator-, and the unary + and -, as well as for operator*(vect<T>, T) and operator*(matrix<T>, T) (product by scalar) and the commutative counterparts operator*(T, vect<T>) and operator*(T, matrix<T>).

It has then to be implemented operator* int the matrix*matrix, matrix*vect, vect*matrix variants.

There are other more sophisticated techniques to avoid the repetition of loops and the local variables inside the functions (like constructing via lambdas, and the vector proxies forming template expressions to be use to get vector slices as matrix transposed views and so on... but may be go too far for what you are just doing)

  • Thank you Sir. It took me a while and another post in this forum but I start to figure out how this code works. You presented a few new concepts that greatly increase the usability of the class. So thank you again for the answer I greatly appreciate it. – Vincent Sep 24 '14 at 9:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.