Trouble with Templates/Classes/Pointers/Exception Handling

I have read the tutorials on this site and now I am testing what I've learned. So far it's not looking good. I have the following code with a template class that creates a matrix of a certain type with a certain amount of rows and columns:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include<exception>
#include<limits>
#include<iostream>

template<class T>
class matrix2D {
    int i,NROWS,NCOLS;
  public:
    T**element;
    matrix2D(int a,int b);
    ~matrix2D();
};

template <class T>
matrix2D<T>::matrix2D(int a,int b)
{
  NROWS=a;
  NCOLS=b;
  try
  {
    element=new T*[NROWS];
    for(i=0;i<NROWS;i++)
      element[i]=new T[NCOLS];
  }
  catch(bad_alloc&)
  {
    std::cout<<"Error allocating memory"<<std::endl<<std::endl<<std::cout<<"Press ENTER to continue...";
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
    exit(1);
  }
}

template <class T>
matrix2D<T>::~matrix2D()
{
  for(i=0;i<NROWS;i++)
    delete element[i];
  delete [] element;
}

int main()
{
    matrix2D<char>A(5,6);
    A.element[0][0]='a';
    std::cout<<A.element[0][0]<<std::endl;
    std::cout<<"Press ENTER to continue...";
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
    return 0;
}


Many compilation errors are showing up:

C:\Users\Chris\Documents\Untitled1.cpp||In constructor 'matrix2D<T>::matrix2D(int, int)':|
C:\Users\Chris\Documents\Untitled1.cpp|25|error: expected type-specifier before 'bad_alloc'|
C:\Users\Chris\Documents\Untitled1.cpp|25|error: expected ')' before '&' token|
C:\Users\Chris\Documents\Untitled1.cpp|25|error: expected '{' before '&' token|
C:\Users\Chris\Documents\Untitled1.cpp|25|error: expected primary-expression before ')' token|
C:\Users\Chris\Documents\Untitled1.cpp|25|error: expected ';' before ')' token|
||=== Build finished: 5 errors, 0 warnings ===|

I am using Code::Blocks 10.05 on Windows Vista. Thanks.

EDIT: Nevermind, I only needed to includestd:: before bad_alloc& and also the header file <cstdlib>.
Last edited on
What I see:

Line 25 probably needs to use std::bad_alloc (missing the namespace).
Line 37 needs to be delete[] too.
I don't see an operator[] overload. This means that lines like 44 and 45 probably won't work. See if you can overload like this:

1
2
3
4
5
//The setter for line 44.  But syntax would change to A.element[0, 0].
T& matrix2D<T>::operator[](int row, int col);
//The getter for line 45.  Same syntax change.  This returns a copy of the value.  Consider changing
//to const T& if you want to return a const reference instead for performance purposes.
const T matrix2D<T>::operator[](int row, int col) const;
Thanks for the reply. I am confused about your suggestion for line 37. Wouldn't that just be repeating line 38? Should I put delete [] element[i]?

Also, lines 44 and 45 seem to be working fine, but perhaps it wouldn't work with all compilers. So I'd like some more help understanding what you mean with overloading the [] operator. You included two operator[] overload declarations. Were you just showing me alternatives or do I need both declarations? Also, what would the definitions of an operator[] overload look like?

Did you mean to write const T& matrix2D<T>::operator[](int row,int col) const; with an ampersand after the first T? As far as I understand, the const at the end means the function cannot change any members of the class, right?...but the concept of a function returning an object by reference, constant or non-constant, confuses me completely. What is the point? To return the variable itself and not a copy? Thanks.

If it's not too much to ask, can you include the code you wrote above inside of my code so I can better understand where it belongs?
Last edited on
Yes, it should be delete[] element[i] because element[i] was allocated using new[], not new (line 23).

Yes, I was thinking you were trying to do a[0][0], not A.element[0][0]. I know even I typed .element in my comment, so call that a brain fart. hehe. It was late at at night.

The two overloads are needed if you want to hide the element field. As you can see from the comments, one is the setter and one is the getter.

Example of the getter:

1
2
3
4
const T matrix2D<T>::operator[](int row, int col) const
{
    return element[row][col];
}


And actually the definition of the setter would be exactly the same.
You can go either way as I stated in the comment. Without the ampersand, you create a copy. That's the difference. Actually, if you use const T, use const T&, but if you return a copy, just make it T. My bad in there too.
You can go either way as I stated in the comment. Without the ampersand, you create a copy. That's the difference. Actually, if you use const T, use const T&, but if you return a copy, just make it T. My bad in there too.


What are the advantages/disadvantages of choosing one way over the other?

Here is my code so far (I added another function that prints out the entire matrix, disregard that. Also, the while loop is just something I added to keep the program running so I could test it without having to close and reopen it):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include<typeinfo>
#include<limits>
#include<cstdlib>
#include<iostream>

template<class T>
class matrix2D {
    int i,j,NROWS,NCOLS;
  public:
    T**element;
    void printout();
    T& operator[](int,int);
    const T& operator[](int,int) const;
    matrix2D(int,int);
    ~matrix2D();
};

template <class T>
matrix2D<T>::matrix2D(int a,int b)
{
  NROWS=a;
  NCOLS=b;
  try
  {
    element=new T*[NROWS];
    for(i=0;i<NROWS;i++)
      element[i]=new T[NCOLS];
  }
  catch(std::bad_alloc&)
  {
    std::cout<<std::endl<<std::endl<<"Error allocating memory"<<std::endl<<std::endl<<std::cout<<"Press ENTER to continue...";
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
    exit(1);
  }
  if(typeid(T)==typeid(char))
  {
    for(i=0;i<NROWS;i++)
    {
    for(j=0;j<NCOLS;j++)
      element[i][j]='0';
    }
  }
  else
    for(i=0;i<NROWS;i++)
    {
    for(j=0;j<NCOLS;j++)
      element[i][j]=0;
    }
}

template <class T>
matrix2D<T>::~matrix2D()
{
  for(i=0;i<NROWS;i++)
    delete [] element[i];
  delete [] element;
}

template <class T>
void matrix2D<T>::printout()
{
  for(i=0;i<NROWS;i++)
  {
    for(j=0;j<NCOLS;j++)
      std::cout<<element[i][j]<<" ";
    std::cout<<std::endl;
  }
}

template <class T>
T& matrix2D<T>::operator[](int row,int col)
{
  return element[row][col];
}

template <class T>
const T& matrix2D<T>::operator[](int row,int col) const
{
  return element[row][col];
}

int main()
{
  int x,y;
  while(true)
  {
    std::cout<<"Enter number of rows: ";
    std::cin>>x;
    std::cout<<std::endl<<"Enter number of columns: ";
    std::cin>>y;
    matrix2D<char>A(x,y);
    A[0,0]='a';
    std::cout<<std::endl<<A[0,0]<<std::endl<<std::endl;
    A.printout();
    std::cout<<std::endl<<std::endl;
  }

  std::cout<<"Press ENTER to continue...";
  std::cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
  return 0;
}


I am getting the following errors now:

C:\Users\Chris\Documents\Untitled1.cpp|12|error: 'T& matrix2D<T>::operator[](int, int)' must take exactly one argument|
C:\Users\Chris\Documents\Untitled1.cpp|13|error: 'const T& matrix2D<T>::operator[](int, int) const' must take exactly one argument|
C:\Users\Chris\Documents\Untitled1.cpp|71|error: 'T& matrix2D<T>::operator[](int, int)' must take exactly one argument|
C:\Users\Chris\Documents\Untitled1.cpp|77|error: 'const T& matrix2D<T>::operator[](int, int) const' must take exactly one argument|
C:\Users\Chris\Documents\Untitled1.cpp||In function 'int main()':|
C:\Users\Chris\Documents\Untitled1.cpp|92|error: no match for 'operator[]' in 'A[(0, 0)]'|
C:\Users\Chris\Documents\Untitled1.cpp|93|error: no match for 'operator[]' in 'A[(0, 0)]'|
||=== Build finished: 6 errors, 0 warnings ===|


Again, thank you for taking the time to answer my many questions. If you have any suggestions for how I could improve other parts of my code, please say. I wasn't sure what was the best way to go for the initialization part of the constructor. I use typeid to perform different initializations based on the type that is passed to the class, but should I instead create function template specializations? function overloads? class template specialization?
Last edited on
I see. I have never tried to overload operator[] for more than one argument ever, so I really wasn't aware that it had this limitation. :-( Sorry. I just didn't know you couldn't throw in more than one argument to this operator. Too bad. To work around that limitation, see if you can overload operator(). That should work, although yes, it is not quite the same. Or you could work out a helper class that also overloads operator[] to complete the illusion and still be able to use A[0][0]. Your call.

So what to return: const T& or T? Depends on what you need. The former avoids creating a copy, so it saves memory, but the caller receives a constant copy (to comply with const correctness), which might not be desirable; the latter returns a copy of the item and the caller is free to do anything with it, but it is a copy and therefore memory is consumed (and processor cycles to create this copy).

So choose what you need.
Yes, I was researching and ran across a forum that talked about helper classes. I kind of understand the idea (the overload operator of the matrix class returns an un-named object of the helper class by reference, and the helper class overloads the [] operator as well....I think). But I don't know then how to access the element member of my matrix2D class from the helper class' operator[] member. The this keyword is a pointer to the object whose member function is being called, but it seems that I need a pointer to the object (A) which returned the helper class by reference. That probably doesn't make much sense. Here's my code now (I've tried to simplify it as much as possible, but there are a lot of definitions I needed to include):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include<exception>
#include<cstdlib>
#include<limits>
#include<iostream>

template <class T> //Helper class.
class helper {
  public:
  helper(int); //Constructor declaration.
  T& operator[](int); //Setter declaration.
  const T& operator[](int) const; //Getter declaration.
};

template <class T> //Constructor definition.
helper<T>::helper(int)
{
  //Not sure what to do here.
}
template <class T> //Setter definition.
T& helper<T>::operator[](int)
{
  //Not sure what to do here.
}

template <class T> //Getter definition.
const T& helper<T>::operator[](int)
{
  //Not sure what to do here.
}
template <class T> //Main class.
class matrix2D {
    int NROWS,NCOLS;
    T**element;
  public:
    matrix2D(int,int); //Constructor declaration.
    helper& operator[](int); //Setter declaration.
    const helper& operator[](int); //Getter declaration.
};

template <class T> //Constructor definition.
matrix2D<T>::matrix2D(int a,int b){
  int i;
  NROWS=a;
  NCOLS=b;
  try
  {
    element=new T*[NROWS];
    for(i=0;i<NROWS;i++)
      element[i]=new T [NCOLS];
  }
  catch(std::bad_alloc&)
  {
    std::cout<<"Error allocating memory"<<std::endl<<std::endl<<std::cout<<"Press ENTER to continue...";
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
    exit(1);
  }
}

template <class T> //Setter definition.
helper& matrix2D<T>::operator[](int a)
{
  return helper(a); //Not sure if this is the right approach.
}

template <class T> //Getter definition.
const helper& matrix2D<T>::operator[](int a) const
{
  return helper(a); //Not sure if this is right approach.
}

int main()
{
    matrix2D<char>A(5,6);
    A[0][0]='a';
    std::cout<<A[0][0]<<std::endl;
    std::cout<<"Press ENTER to continue...";
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
    return 0;
}


I am having trouble understanding what to put for most of the definitions.
Last edited on
The simplest would be (abbreviated for simplicity):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
template<class T>
class matrix2D
{
    T** data;

public:
    //I am unsure if you need this here:  template<class T>, or even class U in case
    //the compiler cannot reuse the "T".
    class RowData
    {
        friend class matrix2D<T>;
        T* _theRow;
        RowData(T* theRow) : _theRow(theRow)
        { }
        //Don't delete[] the row as it is managed by the matrix2D class, so no destructor.
        //You might want to disallow operator=, but not copy construction.  Or not.  Your call.
    public:
        T operator[](int col)
        {
            return _theRow[col];
        }
    };
    //Now the rest of the matrix2D class.  The operator[] of this class would construct
    //a RowData object for the requested row and then return it.
};
Last edited on
I don't understand the syntax of line 13. What does : _theRow(theRow) do? Also, will my constructor definition for matrix2D be the same as I have it (except it seems you replaced T**element with T**data)? What happened to the getters/setters?
The code is abbreviated, meaning I didn't add the whole thing because it wasn't relevant.

Line 13's syntax is an initializer list. Read a proper C++ class tutorial to understand it. Yes, I used data instead of element. The name is irrelevant. Use what you feel is best for you.
Awesome, I think I'm very close to understanding all of this. In fact, I've got a working program:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include<typeinfo>
#include<limits>
#include<cstdlib>
#include<iostream>

template<class T>
class matrix2D {
    int i,j,NROWS,NCOLS;
    T**data;
  public:
    void printout();
    matrix2D(int,int);
    class RowData {
        friend class matrix2D<T>;
        T* _theRow;
        RowData(T* theRow) : _theRow(theRow) {}
      public:
        T& operator[](int col) {return(_theRow[col]);}
        const T& operator[](int col) const {return(_theRow[col]);}
    };
    RowData operator[](int row) {return(RowData(data[row]));}
    const RowData& operator[](int row) const {return(RowData(data[row]));}
};

template <class T>
matrix2D<T>::matrix2D(int a,int b)
{
  NROWS=a;
  NCOLS=b;
  try
  {
    data=new T*[NROWS];
    for(i=0;i<NROWS;i++)
      data[i]=new T[NCOLS];
  }
  catch(std::bad_alloc&)
  {
    std::cout<<std::endl<<std::endl<<"Error allocating memory"<<std::endl<<std::endl<<std::cout<<"Press ENTER to continue...";
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
    exit(1);
  }
  if(typeid(T)==typeid(char))
  {
    for(i=0;i<NROWS;i++)
    {
    for(j=0;j<NCOLS;j++)
      data[i][j]='0';
    }
  }
  else
    for(i=0;i<NROWS;i++)
    {
    for(j=0;j<NCOLS;j++)
      data[i][j]=0;
    }
}

template <class T>
void matrix2D<T>::printout()
{
  for(i=0;i<NROWS;i++)
  {
    for(j=0;j<NCOLS;j++)
      std::cout<<data[i][j]<<" ";
    std::cout<<std::endl;
  }
}

int main()
{
  matrix2D<char>A(5,15);
  A[3][10]='a';
  std::cout<<A[3][10]<<std::endl<<std::endl;
  A.printout();
  std::cout<<std::endl<<std::endl;
  std::cout<<"Press ENTER to continue...";
  std::cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
  return 0;
}


It wouldn't work though until I changed the return type on line 21 from RowData& to RowData. I don't know why. Also, why'd you suggest that I disallow operator= for class RowData?

As an aside, I read you bio and I found it quite funny. I find myself in a similar predicament right now; I am studying chemical engineering but growing ever more interested in programming. Strange.
Delete line 22 and make line 21 a const operator. You don't want a setter version because you don't have storage for RowData objects. It is just a helper class whose only purpose is to provide operator[].

The return type cannot be RowData& (by reference) because you are returning a temporary object that won't outlive the operator call, so it would be pretty much pointless and illegal.

Oh, and I suggested to disallow operator= because that way you disallow users of your class from messing up (or tying to) with the object of the helper class. It is no big deal, just a mere refinement.
Last edited on
So in place of lines 21 and 22, I wrote:

RowData operator[](int row) const {return(RowData(data[row]));}

I couldn't make the return type constant, if that's what you meant by a const operator.

In any case, I'm extremely grateful for all of your help, and I've learned a lot of new information from this discussion. But I don't suppose this code would ever be used in a "real" program since it seems large and template functions can't be stored in separate libraries. If I ever wanted to use it, I'd have to include it in the same source code as the main project, right? Also, I thought friend functions/classes were generally looked down upon in OOP.
Last edited on
Topic archived. No new replies allowed.