4

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?

Vincent
  • 143
  • I didn't understand what you were missing exactly with the class implementation. – StoryTeller - Unslander Monica Sep 14 '14 at 08:25
  • The problem I had was that I realised that for the class implementation everytime I want to perform some vector operations I would need to do quite a bit of operator overloading for a class which basically already has them overladed. In particular the " = " and the "[]" operators which are important for vector operations. The vector class has them already overloaded, so are there any benefits of putting an vector class object in a separate class and overload the operators again. – Vincent Sep 14 '14 at 08:35
  • Let me rephrase the question: are there any benefits of creating a class "Vector2" which performs arithmetic operation on a vector object, compared to free functions that do the same operations. – Vincent Sep 14 '14 at 08:40
  • 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. –  Sep 14 '14 at 07: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 07: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. –  Sep 14 '14 at 07: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 07:42
  • @niemiro - so do I get you right when I understand you comment that in this case it makes no sense to write a seperate class? Actually I was leaning towards that but I don't trust my judgement (yet). thx – Vincent Sep 14 '14 at 08:48
  • Hallo gnat, thank you for making me aware of the flag. I did not know about it. Just flagged the question. Hope it works. – Vincent Sep 24 '14 at 09:20
  • If you have a collection of related "free" functions you should strongly consider putting them in a namespace so as not to pollute the global namespace. – YoungJohn Sep 24 '14 at 14:32
  • @John good point have not considered that. – Vincent Sep 24 '14 at 15:04
  • It is very difficult to produce a useful linear algebra library. It is even more difficult to produce a linear algebra library that is more useful than the libraries that have already been written and refined. Have you looked at 'R' and other mathematical systems? – kevin cline Sep 25 '14 at 02:14
  • hi kevin. I did. That is in fact where I came from. Thus I am used to a certain degree of usability (which R certainly provides). The library is not supposed to for sale but for personal usage. Thus it is not required to include extensive safty mechanisms to ensure smooth functioning of the library . It's for me to work with. As a c++ beginner however I must say this is an excellent exercise. It makes you really think what problems there are in designing a library and to look and find the solutions for them. So far I learned a lot and it's a really great experience. – Vincent Sep 25 '14 at 05:22

4 Answers4

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;
dgnuff
  • 189
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)

Emilio Garavaglia
  • 4,299
  • 1
  • 22
  • 23
  • 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 09:12
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 07:57
  • My pleasure :-) –  Sep 14 '14 at 08:01