I am writing a vector class and I would like it to have the following characteristics:
- Use static allocation on the stack whenever possible (to avoid calling new for efficiency).
- Be able to be instantiated from a pointer if the user prefers to provide a previously allocated array.
- The class needs to be easily converted to a simple pointer. This allows to use previously written routines in C.
Find below this simple test problem with the solution I came up with. I use inheritance so Vector inherits from Vector_base which provides a common interface (pure virtual) for all vectors. Then I define an empty class Vector that allows me then using partial specialization to have different storage schemes; static or dynamic.
The idea behind this is that I just want vector to be a C++ wrapper to the old-fashioned static array.
I like the implementation below. I'd like to keep the interface I came up with in main.
What I don't like is that sizeof(Vector3) = 32 when in C a vector of three doubles is 24 bytes. The reason for this is the extra 8 bytes of the virtual table.
My question: can I somehow come up with another design that would provide me with the same interface but the vector only has 24 bytes?
Summarizing:
- I'd like have a Vector3 of 24 bytes, as in C.
- I still want to have arbitrarily large vectors though (with
<double,n>
) - I'd like to keep the interface used in main().
Could I use a programming idiom like traits or polices for this? I am very new to those and I don't know if they could provide a solution.
Find my little test code below:
#include <iostream>
using namespace std;
#define TRACE0(a) cout << #a << endl; a;
#define TRACE1(a) cout << #a "=[" << a << "]" << endl;
enum alloc_type {Static,Dynamic};
template <class T>
class Vector_base{
public:
Vector_base(){}
virtual operator T*() = 0;
virtual T operator[](int i)const = 0;
virtual T& operator[](int i) = 0;
virtual int size() const = 0;
friend ostream& operator<<(ostream &os,const Vector_base& v){
for (int i=0; i<v.size(); i++)
cout << v[i] << endl;
return os;
}
};
// base template must be defined first
template <class T, int n,alloc_type flg=Static>
class Vector{};
//Specialization for static memory allocation.
template <class T, int n>
class Vector<T,n,Static>: public Vector_base<T>{
public:
T a[n];
public:
Vector() {
for (int i=0; i<n; i++) a[i] = 0;
}
int size()const{return n;}
operator T*(){return a;}
T operator[](int i)const {return a[i];}
T& operator[](int i){return a[i];}
};
//Specialization for dynamic memory allocation
template <class T,int n>
class Vector<T,n,Dynamic>: public Vector_base<T>{ //change for enum. flg=0 for static. flg=1 for dynamic. Static by default
public:
T* a;
public:
Vector():a(NULL){
}
Vector(T* data){ //uses data as its storage
a = data;
}
int size()const{return n;}
operator T*(){return a;}
T operator[](int i)const {return a[i];}
T& operator[](int i){return a[i];}
};
//C++11 typedefs to create more specialized three-dimensional vectors.
#if (__cplusplus>=201103L)
template <typename Scalar,alloc_type flg=Static>
using Vector3 = Vector<Scalar,3,flg>;
#else
#error A compiler with the C++2011 standard is required!
#endif
int main(){
cout << "Testing Vector3: " << endl;
//Vector<double,3> v3;
Vector3<double> v3;
TRACE0(cout << v3 << endl;);
TRACE1(sizeof(v3));
//Vector<double,3,Dynamic> v0(v3);
Vector3<double,Dynamic> v0(v3); //calls Vector<double,3,Dynamic>::Vector(double*) and uses the conversion operator on v3.
TRACE1(sizeof(v0));
TRACE1(sizeof(double*));
TRACE0(v3[1] = 2.1;);
TRACE0(cout << v0 << endl;);
return 0;
}