|
 |
|
 |
01-30-2005, 07:56 PM
|
#1 (permalink)
|
|
Registered User
Join Date: Jan 2005
Posts: 7
|
C++ Multi Dimensional Array using Vectors.
Hi, I am trying to create a 2d vector, and have it so I can access it much like an 2d array (ie array[][]).
I stumbled across this implementation and was trying to get it to work ( original code)
i changed it a bit...
Code:
#include <vector>
using namespace std;
template <class T>
class C2DVector
{
public:
template<typename _T> friend ostream& operator << (ostream&, C2DVector<_T>);
C2DVector():m_dimRow(0), m_dimCol(0){;}
C2DVector(int nRow, int nCol) {
m_dimRow = nRow;
m_dimCol = nCol;
for (int i=0; i < nRow; i++){
vector<T> x(nCol);
int y = x.size();
m_2DVector.push_back(x);
}
}
void SetAt(int nRow, int nCol, const T& value) {
if(nRow >= m_dimRow || nCol >= m_dimCol)
; // throw out_of_range("Array out of bound");
else
m_2DVector[nRow][nCol] = value;
}
T GetAt(int nRow, int nCol) {
if(nRow >= m_dimRow || nCol >= m_dimCol)
cerr<<"array out of bounds"<<endl;//throw out_of_range("Array out of bound");
else
return m_2DVector[nRow][nCol];
}
void GrowRow(int newSize) {
if (newSize <= m_dimRow)
return;
m_dimRow = newSize;
for(int i = 0 ; i < newSize - m_dimCol; i++) {
vector<int> x(m_dimRow);
m_2DVector.push_back(x);
}
}
void GrowCol(int newSize) {
if(newSize <= m_dimCol)
return;
m_dimCol = newSize;
for (int i=0; i <m_dimRow; i++)
m_2DVector[i].resize(newSize);
}
vector<T>& operator[](int x) {
return m_2DVector[x];
}
private:
vector< vector <T> > m_2DVector;
unsigned int m_dimRow;
unsigned int m_dimCol;
};
template <typename T>
ostream& operator << (ostream& os, C2DVector<T> test)
{
for (int i=0; i < test.m_dimRow; i++){
for (int j=0; j < test.m_dimCol; j++){
os << test[i][j] << " ";
}
os << endl;
}
return os;
}
so here is how Im trying to use it...
Code:
#include <iostream>
#include <vector>
#include "mvector.cc"
using namespace std;
struct object
{
//For now, lets test on a 5x5 matrix
object()
{
array = new C2DVector<int>(5,5);
}
C2DVector<int> *array;
};
int main()
{
//Lets try creating it by hand.
C2DVector<int>a(3,3);
a[0][0]=1;
a[0][1]=1;
a[0][2]=1;
a[1][0]=1;
a[1][1]=1;
a[1][2]=2;
a[2][0]=1;
a[2][1]=3;
a[2][2]=4;
cout<<a<<endl;
// It works correctly.
object * start;
start = new object;
start->array->SetAt(1,1,1);//Works
cout<< *start->array<<endl;
*start->array[1][2]=2;//Does not work!
return 0;
}
I'm not sure whyi can't get the [][] access to work. I would think the overloaded:
vector<T>& operator[](int x) would let me gain access to column's of the 2dvector.
I can't even compile with *start->array[1][2]=2 . I thought this line means, I'm accessing the value of the array, start points to, at [1][2] and assigning it to 2.
Any insight would be helpful. thanks!
|
|
|
01-31-2005, 05:50 AM
|
#2 (permalink)
|
|
[code][/code] enforcer
Join Date: Mar 2003
Location: Netherlands
Posts: 1,545
|
I'll take a peek at it in a few hours if no one beat me to it.
__________________
|
|
|
01-31-2005, 02:07 PM
|
#3 (permalink)
|
|
[code][/code] enforcer
Join Date: Mar 2003
Location: Netherlands
Posts: 1,545
|
Why not making your life easier?
Code:
#ifndef C2DMATRIX_H
#define C2DMATRIX_H
#include <vector>
#include <string>
#include <iostream>
#include <stdexcept>
using namespace std;
class C2DMatrixError
{
public:
C2DMatrixError(std::string msg) : sMsg_(msg)
{}
virtual std::string what() = 0;
protected:
std::string sMsg_;
};
//--
class BadSize : public C2DMatrixError
{
public:
BadSize(std::string msg) : C2DMatrixError(msg)
{}
std::string what()
{
return sMsg_;
}
};
//--
class BoundsViolation : public C2DMatrixError
{
public:
BoundsViolation(std::string msg) : C2DMatrixError(msg)
{}
std::string what()
{
return sMsg_;
}
};
//--
template<typename T>
class C2DMatrix
{
public:
C2DMatrix() {}
C2DMatrix(unsigned, unsigned );
T& operator() (unsigned, unsigned);
const T& operator() (unsigned i, unsigned j) const;
size_t nRows_;
size_t nCols_;
private:
vector<vector<T> > data_;
template <typename _T>
friend ostream& operator<<(ostream& os, C2DMatrix<_T>& c2d );
};
//---------------------------
template<typename T>
C2DMatrix<T>::C2DMatrix(unsigned rows, unsigned cols)
{
if (rows == 0 || cols == 0)
{
throw BadSize("ERROR: Bad size");
}
data_.resize(rows);
nRows_=rows;
nCols_=cols;
for (unsigned i = 0; i < rows; ++i)
{
data_[i].resize(cols);
}
}
//----------------------------
template<typename T>
inline T& C2DMatrix<T>::operator() (unsigned row, unsigned col)
{
if (row >= nRows_ || col >= nCols_ )
{
throw BoundsViolation("ERROR in non-const operator(): Bounds Violation");
}
return data_[row][col];
}
//----------------------------
template<typename T>
inline const T& C2DMatrix<T>::operator() (unsigned row, unsigned col) const
{
if (row >= nRows_ || col >= nCols_)
{
throw BoundsViolation("ERROR in const operator()const: Bounds Violation");
}
return data_[row][col];
}
//-----------------------------
template <typename T>
ostream& operator<< (ostream& os, C2DMatrix<T>& c2d)
{
for (int i=0; i < c2d.nRows_; i++)
{
for (int j=0; j < c2d.nCols_; j++)
{
os << c2d(i,j) << " ";
}
os << endl;
}
return os;
}
#endif //C2DMATRIX_H
Code:
#include "C2DArray.h"
#include <iostream>
#include <cstdlib>
#include <vector>
using namespace std;
class object
{
public:
object() : array(C2DMatrix<int>(1,1))
{ }
C2DMatrix<int> array;
};
int main()
{
try
{
object start;
cout<<start.array<<endl;
start.array(1,1)=1;
cout<<start.array<<endl;
}
catch(C2DMatrixError& err)
{
cout<<err.what()<<endl;
}
return 0;
}
__________________
|
|
|
01-31-2005, 02:10 PM
|
#4 (permalink)
|
|
[code][/code] enforcer
Join Date: Mar 2003
Location: Netherlands
Posts: 1,545
|
*edit*
__________________
|
|
|
01-31-2005, 04:04 PM
|
#5 (permalink)
|
|
Registered User
Join Date: Jan 2005
Posts: 7
|
Cool! The code is much cleaner. I just need to brush up on exeception handling, but I think can probably write my own implementation in the future now. A few questions though...
1)
IF I were to ever try to do what I was doing in my original post ( access that array via [ ] [ ]). How would I do it? Even though i wouldn't use that implementation anymore, it bugs me that I can't get the syntax for it to work. What's wrong with *start->array[1][2]=2; ?
2) What naming convetion are you using for your code? I figure it's a good time to start organizing my code a bit.
Thanks!
|
|
|
01-31-2005, 04:11 PM
|
#6 (permalink)
|
|
[code][/code] enforcer
Join Date: Mar 2003
Location: Netherlands
Posts: 1,545
|
Quote:
|
I just need to brush up on exeception handling
|
The class C2DMatrixErroris a base class for specialized errors.
All I need to do is throw derrived classes from it as it fits my needs.
All I need to do is catch a C2DMatrixError& class. I've provided every class with an own what() member, derrived from the base class virtual what() member. so the base class object will always return the right version of what().
Caveat:
Obseverve the ampersand (&) in C2DMatrixError& closely (see main() function). Otherwise we won't enjoy polymorphism.
Tips:
- All what() versions for the derrived classes are the same, so you could have defined it in the base class only. However, you can specialize your what() members.
- This is better (add const): catch(const C2DMatrixError& err) {//...}
- This is better: void what() const;
- Observe how I overloaded operator()(). That is a new style. Doesn't mean I endorse it with all my heart.
- Observe how I overloaded TWO versions of operator()(). It is always a good idea that these overloads come in pairs: a const and a non-const version. Otherwise you won't be able to pass const types. Same goes when overloading operator[]() obvoiusly since this is the mother of our idea we have in this excercise.
- Remove the "cerr<<" from the class. A container shouldn't be responsible for output to anything. A container should contain
Quote:
|
IF I were to ever try to do what I was doing in my original post ( access that array via [ ] [ ]). How would I do it? Even though i wouldn't use that implementation anymore, it bugs me that I can't get the syntax for it to work. What's wrong with *start->array[1][2]=2; ?
|
From your original posted code, this works. See what happens.
Code:
object* obj = new object;
(*obj->array)[0][0]=12;
(*obj->array)[1][1]=24;
cout<<(*obj->array)[0][0]<<endl;
cout<<(*obj->array)[0][0]<<endl;
cout<<(*obj->array)<<endl;
Try it without encapsulation first to see it more clearly:
Code:
C2DVector<int> *array = new C2DVector<int>(5,5);
(*array)[0][0]=234;
cout<<(*array)[1][1]<<endl;
1b) Creating a STL container on the heap (using new) is hard to handle. Don't do that. Unless you need thousands of them.
2a) members: name_
2a2) std::string members: sName_
2a3) members as counter: nName_
2b) functions: some_function()
2c) classes: UppercaseName
Nothing fancy.
__________________
Last edited by Valmont; 01-31-2005 at 05:45 PM.
|
|
|
02-01-2005, 12:48 AM
|
#7 (permalink)
|
|
[code][/code] enforcer
Join Date: Mar 2003
Location: Netherlands
Posts: 1,545
|
Oops, in my original code change to this (I initialized the matrix wrong):
Code:
class object
{
public:
object() : array(2,2) // this has been changed now.
{ }
C2DMatrix<int> array;
};
__________________
|
|
|
02-01-2005, 06:32 AM
|
#8 (permalink)
|
|
[code][/code] enforcer
Join Date: Mar 2003
Location: Netherlands
Posts: 1,545
|
And you'd like to see perhaps some output optimization for HUGE matrices. This technique comes in handy for N x N matrices where hundreds of thousands, millions and more elements could be created:
Code:
template <typename T>
class print_vector
{
public:
void operator()(const std::vector<T>& v)
{
std::copy(v.begin(), v.end(), std::ostream_iterator<T>(std::cout, " "));
std::cout<< " " << std::endl;
}
};
//-------------------------------
template <typename T>
ostream& operator<< (std::ostream& os, C2DMatrix<T>& c2d)
{
typename std::vector<std::vector<T> >::iterator it_end = c2d.get_internal_rep().end();
typename std::vector<std::vector<T> >::iterator it_begin = c2d.get_internal_rep().begin();
std::for_each( it_begin, it_end, print_vector<T>() );
return os;
}
Don't forget to make an accessor:
Code:
template <typename T>
C2DMatrix<T>::vector<vector<T> >& get_internal_rep()
{
return data_;
}
__________________
|
|
|
02-01-2005, 01:16 PM
|
#9 (permalink)
|
|
Registered User
Join Date: Jan 2005
Posts: 7
|
im not sure i understand how that is optimized...
the << operator you are still going through the vector and printing out each element. but i'm guessing that copying the elements directly to ostream cout is faster?
|
|
|
02-01-2005, 03:01 PM
|
#10 (permalink)
|
|
[code][/code] enforcer
Join Date: Mar 2003
Location: Netherlands
Posts: 1,545
|
Bulk operations will benefit from the stream iterators greatly. As it happens to be they behave like a container, so we can use them quite elgantly. They are fast for mass storage/retreival.
Tip:
- Don't forget to overload your extraction/insertion operators. It is crucial as you will obviously see.
- Learn to include the <iterator> header. The standard requires it, yet a only a relative few include them. The fact code often works without the include is bad luck: compiler designers mistake.
- Check out: istreambuf_iterator<> as well.
- Check out: back_insert_iterator<>() as well.
Code:
#include <iostream>
#include <vector>
#include <fstream>
#include <string>
#include <iterator>
#include <algorithm>
// istream_iterator, ostream_iterator bulk operations demo.
// 1) We create a file of martian data, by stream a vector towards ostream.
// 2) Then we empty the vector.
// 3) We load the file back into the vector and view its contents.
using namespace std;
//--
struct Martian
{
int age_;
bool gender_;
string species_;
};
//--
ostream& operator<<(ostream& os, const Martian& md)
{
os<<md.age_<<" "<<md.gender_<<" "<<md.species_;
return os;
}
//--------------------------------------------------
istream& operator>>(istream& is, Martian& md)
{
is>>md.age_>>md.gender_;
//So the species (std::string) name can have spaces.
getline(is, md.species_);
return is;
}
//--------------------------------------------------
void save_db(vector<Martian>& mguide)
{
ofstream outfile("MartianData.txt");
copy(mguide.begin(), mguide.end(), ostream_iterator<Martian>(outfile, "\n") );
outfile.close();
}
//--------------------------------------------------
void open_db(vector<Martian>& mguide)
{
ifstream infile("MartianData.txt");
copy( istream_iterator<Martian>(infile), istream_iterator<Martian>(),
back_inserter(mguide) );
infile.close();
}
//--------------------------------------------------
int main(int argc, char *argv[])
{
Martian theMd;
vector<Martian> martianGuide;
//Let's create some martian info and dump it in a vector.
theMd.age_ = 320;
theMd.gender_ = false;
theMd.species_ = "Crater Planktons";
martianGuide.push_back(theMd);
theMd.age_ = 243;
theMd.gender_ = false;
theMd.species_ = "Mountain Worms";
martianGuide.push_back(theMd);
theMd.age_ = 211;
theMd.gender_ = true;
theMd.species_ = "Sky Fish";
martianGuide.push_back(theMd);
//Let's save it to a file.
save_db(martianGuide);
//Emtpy this vector.
martianGuide.erase(martianGuide.begin(), martianGuide.end() );
//Fill the vector again to verify correct storage and operator>> overload.
open_db(martianGuide);
copy(martianGuide.begin(), martianGuide.end(), ostream_iterator<Martian>(cout, "\n"));
return 0;
}
__________________
|
|
|
02-02-2005, 06:22 PM
|
#11 (permalink)
|
|
Registered User
Join Date: Jan 2005
Posts: 7
|
the more i learn, the more questions i have...
^Looks interesting.. I'll have to look over it more when I have time this weekend.
Another question...
Is there something special with templates? By special, I mean some sort of conversion... because when I took the C2DMatrix class you wrote, and changed it from a template to type 'int', I can no longer execute:
Code:
object start; //so start.array is hardcoded to be of type 'int'
start.array(1,1) = 1;
I get a "error: non-lvalue in assignment" error.
|
|
|
02-02-2005, 11:11 PM
|
#12 (permalink)
|
|
[code][/code] enforcer
Join Date: Mar 2003
Location: Netherlands
Posts: 1,545
|
Originally your code created an instance of struct "object".
(Don't name your structs, classes, and types with only lower case letters next time).
Code:
object * start;
start = new object;
And your struct set us up a matrix of ints.
Code:
struct object
{
//For now, lets test on a 5x5 matrix
object()
{
array = new C2DVector<int>(5,5);
}
C2DVector<int> *array;
};
That means: that is the intention, looking at the declaration of C2DVector<int>*.
But this code is faulty anyway (although compilers accept it!).
So I've adapted it:
Code:
class object
{
public:
object() : array(2,2) // this has been changed now.
{ }
C2DMatrix<int> array;
};
So I haven't changed anything. If I did, then your struct is ambigious.
All in all, this stuct of int-matrices work perfectly:
Code:
class object
{
public:
object() : array(2,2)
{ }
C2DMatrix<int> array;
};
int main()
{
try
{
object start;
start.array(0,0)=1;
start.array(1,1)=2;
cout<<start.array<<endl;
}
catch(C2DMatrixError& err)
{
cout<<err.what()<<endl;
}
return 0;
}
__________________
|
|
|
02-02-2005, 11:41 PM
|
#13 (permalink)
|
|
[code][/code] enforcer
Join Date: Mar 2003
Location: Netherlands
Posts: 1,545
|
Wait a minute...
How did you change the matrix class? Post it please.
__________________
|
|
|
02-03-2005, 12:00 AM
|
#14 (permalink)
|
|
Registered User
Join Date: Jan 2005
Posts: 7
|
i think you misunderstood my question...
i took the template...
Code:
template<typename T>
class C2DMatrix
{
public:
C2DMatrix() {}
C2DMatrix(unsigned, unsigned );
T& operator() (unsigned, unsigned);
const T& operator() (unsigned i, unsigned j) const;
size_t nRows_;
size_t nCols_;
vector<vector<T> > data_;
private:
template <typename _T>
friend ostream& operator<<(ostream& os, C2DMatrix<_T>& c2d );
};
and changed the class to 'int' type..
Code:
class C2DMatrix
{
public:
C2DMatrix() {}
C2DMatrix(unsigned, unsigned );
int operator() (unsigned, unsigned);
const int operator() (unsigned i, unsigned j) const;
size_t nRows_;
size_t nCols_;
vector<vector<int> > data_;
private:
friend ostream& operator<<(ostream& os, C2DMatrix& c2d );
};
now assignment (ex: array(int,int) = int ) gives me that lvalue error.
So I was asking if there were anything different about how template classes are interpreted or referenced, compared to a non-template class. My understanding of templates is that the template type is bound at compile time, so I don't see why C2DMatrix<int> array, would be any different from C2DMatrix array (if C2DMatrix was hardcoded to type 'int'). ;p
|
|
|
02-03-2005, 07:04 AM
|
#15 (permalink)
|
|
[code][/code] enforcer
Join Date: Mar 2003
Location: Netherlands
Posts: 1,545
|
I assume you changed the implementation of the class as well. Not only its declaration.
Next step is to change it correctly. You didn't. Observe the details.
There is no difference between changing it to int or keeping it a template. Not even in speed!
__________________
|
|
|
| Thread Tools |
|
|
| Display Modes |
Linear Mode
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -8. The time now is 12:48 AM.
|
Copyright © 2000-2008, Milano Interactive
Web Hosting provided by Portal 360 Web Hosting
|
 |
|