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?
4 Answers 4
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– Vincent2014年09月14日 11:13:18 +00:00Commented Sep 14, 2014 at 11:13
-
1Ironically, a mathematical vector would best be implemented by means of a
std::array
instead of astd::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– Arne Mertz2014年09月18日 12:00:28 +00:00Commented Sep 18, 2014 at 12:00
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;
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– Vincent2014年09月14日 07:57:37 +00:00Commented Sep 14, 2014 at 7:57
-
My pleasure :-)shakurov– shakurov2014年09月14日 08:01:10 +00:00Commented Sep 14, 2014 at 8:01
The concept behind std::vector
is 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– Vincent2014年09月24日 09:12:40 +00:00Commented Sep 24, 2014 at 9:12
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. ThemyVec.
notation is also likely to be more expected thatfunc(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.Vector
) similar to standard names. Several features of C++11 will help you a big lot.