Relearning C++: Overloading operators, concepts, etc.

I apollogize for those who don't like long posts. If you are one who doesn't, then this post isn't for you. I'm going to include my full header, class source and test file as well as my error output. This is not to bore people to death or spam anyone. I started my programming career off with C++ around 15 years ago. Then I discovered C programming a year or 2 later, and ended up leaving C++ behind. Now that I'm trying to pick it back up, I'm having some trouble recalling how to do certain things when overloading operators and such. I'm thinking that constructive feedback / review may help me on several levels.

I'm currently working on a gentoo linux system with g++ version 4.3.4. I know part of what I seemingly have to do, but not the why of it. So, let me begin with the concept. To get on my feet, I am coding an Array class. Allowing access to and setting of size and real size, overloading of operators = for copying of another array and [] for indexing of array elements. I want the [] operator to allow output of the indexed element or assignment to the indexed element E.G. cout << array[5] << endl; or array[5] = number; One error you'll notice in my code is in an if statement where the > operator isn't defined. I haven't defined the > operator and am honestly stumped as to why I should have to for this template class. If I have an array of integers, and I access an integer and compare it against another integer, then the operator should already know how to compare against these, unless it's because of the [] operator returning a reference to the template T, but I think that's necessary to allow assignments to array[index] is it not? Otherwise, when I say array[index] = number, I'd be saying 10 = number or something similar. instead of assigning number to the memory region it needs to go to, or is my logic flawed?

Another error you'll find is inside the cout statements. You'll see that I've prefixed array[] and array2[] with *. I don't think this is really correct, but without the * dereference, cout complains bitterly and extensively about unknown traights.

If I've been unclear, all constructive criticism is welcome. I'm a very experienced C programmer, but knocking the rust off my C++ is proving teedious. If you can help knock a chunk or two off, take a swing.

Here's a look inside my header:

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
#ifndef __ARRAY_H__
#define __ARRAY_H__
template <class T>
class
  Array
  {
  public:
  Array( void );
  Array( const Array &array );
  ~Array( void );
  Array& operator = (const Array &array);
  T& operator = (const T &item);
    T& operator [] (unsigned int index);
    void Add(const T &item);
    void Clear();
    void Delete(unsigned int pos);
void* GetPointer();
    unsigned int RealSize( void );
    void RealSize(unsigned int NewSize);
    unsigned int Size( void );
    void Size(unsigned int NewSize);
    enum exception { MEMFAIL };
  private:
  T             *       _content;
  unsigned int          _size;
  unsigned int          _real_size;
  const static int      _array_step = 128;
  const static int      _array_multiplier = 2;
  };
#endif /* __ARAY_H__ */ 


And here's the class itself:

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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#include <iostream>
#include <stdexcept>
#include <cstring>
#include <cerrno>
using namespace std;
#include "array.h"
#define ALLOCATION_ERROR cerr << "Error allocating memory in file " << __FILE__ << " on line " << __LINE__ << ": " << strerro$
template <class T>
Array<T>::Array( void )
  {
  _size = 0;
  _real_size = sizeof( T ) * _array_step;
  try
    {
    _content = new T[ _real_size ];
    }
  catch( const exception &e)
    {
    ALLOCATION_ERROR;
    throw MEMFAIL;
    }
  }
template <class T>
Array<T>::~Array( void )
  {
  if (_content)
    {
    delete _content;
    _content = NULL;
    _size = _real_size = 0;
    }
  }
template <class T>
Array<T>::Array(const Array &array)
   {
  _real_size = array.RealSize();
  try
    {
    _content = new T [ _real_size ];
    }
  catch (const exception &e)
    {
    ALLOCATION_ERROR;
    throw MEMFAIL;
    }
  memcpy( _content, array.GetPointer(), _real_size );
  _size = array.Size();
  }
template <class T>
Array<T>& Array<T>::operator = (const Array &array)
  {
  if (this == &array)
    return( *this );
  if (array.Size() == 0)
    Clear();
  Size( array.Size() );
  memcpy( _content, array.GetPointer(), sizeof( T ) * array.Size() );
  return( *this );
  }
template <class T>
T& Array<T>::operator = (const T &item)
  {
  _size = 0;
  _real_size = sizeof( T ) * _array_step;
  try
      {
    if (_content)
      delete _content;
    _content = new T[ _real_size ];
    }
  catch( const exception &e)
    {
    ALLOCATION_ERROR;
    throw MEMFAIL;
    }
  *_content = item;
  _size = _real_size;
  return( *_content );
  }
template <class T>
unsigned int Array<T>::RealSize( void )
  {
  return( _real_size );
  }
template <class T>
void Array<T>::RealSize(unsigned int NewSize)
  {
  T *paged;
  _size = _real_size = NewSize;
  if (_real_size != 0)
    {
    paged = _content;
    try
      {
      _content = new T [ _real_size ];
      }
    catch( const exception &e)
       {
      ALLOCATION_ERROR;
      throw MEMFAIL;
      }
    memcpy( _content, paged, _real_size );
    }
  else
    Clear();
  }
template <class T>
unsigned int Array<T>::Size( void )
  {
  return( _size );
  }
template <class T>
void Array<T>::Size(unsigned int NewSize)
  {
  T *paged;
  _size = NewSize;
  if (_size != 0)
    {
    if ((_size > _real_size) || (_size < _real_size / 2))
      {
      _real_size = _size;
      paged = _content;
      try
        {
        _content = new T [sizeof( T ) * _size];
        }
      catch( const exception &e)
        {
        ALLOCATION_ERROR;
        throw MEMFAIL;
        }
       memcpy( _content, paged, sizeof( T ) * _size );
      }
    }
  else
    Clear();
  }
template <class T>
void Array<T>::Delete(unsigned int pos)
  {
  if (_size == 1)
    Clear();
  else
    {
    for(unsigned int i=pos; i<_size-1; i++)
      _content[i] = _content[i+1];
    --_size;
    }
  }
template <class T>
void Array<T>::Clear( void )
  {
  _size = 0;
  try
    {
    if (_content != NULL)
      delete _content;
    }
  catch( const exception &e)
    {
    _content = NULL;
    }
  _real_size = sizeof( T ) * _array_step;
  _content = new T [ _real_size ];
   }
template <class T>
void* Array<T>::GetPointer( void )
  {
  return( _content );
  }
template <class T>
T& Array<T>::operator [] (unsigned int index)
  {
  return( _content[index] );
  }
template <class T>
void Array<T>::Add(const T &item)
  {
  T * paged;
  ++_size;
  if (_size > _real_size)
    {
    paged = _content;
    _real_size *= _array_multiplier * sizeof( T );
    try
      {
      _content = new T [ _real_size ];
      memcpy( _content, paged, _real_size );
      }
    catch( const exception &e)
      {
      ALLOCATION_ERROR;
      throw MEMFAIL;
      }
    }
  _content[_size-1] = item;
  }


Test program and errors in follow-up post.
Last edited on
Here is the test 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
#include <iostream>
using namespace std;
#include <cstdlib>
#include "array.h"
// function for outputting array items
template <class T>
void output_array(Array<T> *array)
  {
  for(unsigned int i=0; i<array->Size(); i++)
    cout << *array[i] << ", ";
  cout << endl;
  }
int main(void)
  {
  Array<int> * array = new Array<int>;
  Array<int> * array2 = new Array<int>;
  int x;
  // setting array size
  array->Size(15);
  // filling array with pseudo-random values
  for(unsigned int i=0; i<15; i++)
    array[i] = rand() % 10;
  // lets add some values using Add()
  array->Add(7);
  array->Add(94);
  array->Add(1);
  // output all array items
  output_array(array);
  // delete 1-st  and last items
  array->Delete(0);
  array->Delete(array->Size() - 1);
 
  // output all array items (again)
  output_array(array);
  // let's sort all array items using extemly ineffective sort
  for(unsigned int i=0; i<array->Size();)
  if (array[i] > array[i+1])
    {
    x = array[i];
    array[i] = array[i+1];
    array[i+1] = x;
    i = 0;
    continue;
    }
  else
    i++;
  output_array(array);
  // create another array, based on first
  array2->Clear(); // clear it
  // check multiple addition
  for(int i=0; i<1000000; i++)
    array2->Add(rand());
  // check assigment operator
  array = array2;
  // output array
  cout << "array 2 size " << array2->Size() << endl;
  cout << "array 1 size " << array->Size() << endl;
  // check random element
  int testel = rand() % 1000000;
  cout << "array 2 element " << testel << " = " << *array2[testel] << endl;
  cout << "array 1 element " << testel << " = " << *array[testel] << endl;

  delete array;
  delete array2;
 
  // that's all!
  return 0;
  }


And, finally, the errors:

test.cpp: In function 'int main()':
test.cpp:51: error: no match for 'operator>' in '*(array + ((long unsigned
int)(((long unsigned int)i) * 16ul))) > *(array + ((long unsigned int)(((long
unsigned int)(i + 1u)) * 16ul)))'
test.cpp:53: error: cannot convert 'Array<int>' to 'int' in assignment
test.cpp:80: error: no match for 'operator*' in '**(array2 + ((long unsigned
int)(((long unsigned int)testel) * 16ul)))'
test.cpp:81: error: no match for 'operator*' in '**(array + ((long unsigned
int)(((long unsigned int)testel) * 16ul)))'
test.cpp: In function 'void output_array(Array<T>*) [with T = int]':
test.cpp:40: instantiated from here
test.cpp:16: error: no match for 'operator*' in '**(array + ((long unsigned
int)(((long unsigned int)i) * 16ul)))'

I do appreciate any and all help rendered. Thanks in advance.
Last edited on
Ok, please reformat your code using code tags so that we can see line numbers.

Second, I have to apologize for taking so long to reply. I was ROTFL at this statement:


Then I discovered C programming a year or 2 later, and ended up leaving C++ behind.


Third, this operator makes no sense at all:
T& operator = (const T &item);

Fourth, why are you reinventing std::vector<>?

jsmith's last question is the most relevant. Is this just an exercise of practice for you or do you have a reason for this?
EDIT: Also, what's better than C++ in C?
And isn't it ROFL?
Last edited on
Guess I've seen it both ways.
Well, to be fair, 15 years ago C++ wasn't what it is today. Although I'm wondering how exactly you can discover C only after two years of C++.
no reason for the 'T' to be silent except retarded gamers who can't spell, so ROTFL makes more sense IMO. :P
What about the fact that articles and similarly short particles are generally not included in acronyms?
For example, it's "laser", not "labseor".
Regardless ROFL was derived from the interwebs and not the internet so spelling and grammar are irrelevant. helios also raises a good point in that the words the and of are often excluded from acronyms.
Back when I first learned C++, it was 1994 - 1995. I was using MS-DOS 6.22 and Turbo C++. This was back in the time when you included all your header files with the .h suffix. You didn't use namespaces.

I probably learned about two thirds C++ and one third C, but I didn't realize it at the time. I thought it was all C++.

Even though Windows 3.1 had been out and in use for quite some time, I had no Windows experience, and I didn't have any internet experience. It was only in 1996 that I discovered the internet, Windows 3.1, windows 95, C programming, and left C++ behind.

I tried mixing C and C++ with quite interesting results. cout and printf cin and getc and such all work on different levels and speeds apparently. I would have whole sections of input and output running out of order. So, I dropped C++ because C was just so common and there was such a vast amount of program examples in C.

I have had brief occasion to revisit C++ to help friends debug homework programs, and so I'm not totally lost, but as you can probably tell, I'm far from up-to-date.

I'm not out to re-invent the wheel with my program, and I was unaware that I was doing so. Never-the-less, it serves my purposes very well.
Mainly, I'm trying to get my feet under me again when it comes to writing classes, overloading operators, using exception handling, etc and debugging C++ programs. I for one find the errors more cryptic with the C++ compilers when it comes to certain things.
I use pointers a lot in my programs, so it is intentional that I declared array and array2 the way that I did in the test program. However, I imagine this may be part of the problem I'm having with the overloaded [] operator and cout, cerr, etc. If I declared an instance of Array like this:
1
2
Array<int> array;
array.Size( 15 );
;

would accessing the index such as
cout << array[5] << endl;

Be the same as the following?

1
2
3
Array<int> * array = new Array<int>;
array->Size(15);
cout << array[5] << endl;


Or, since the second example is using a pointer, is the index needing to be dereferenced first -- something like one of the following:

1
2
cout << *array[5] << endl;
cout << array->[5] << endl; // This feels too much like perl programming!  




Last edited on
Or, since the second example is using a pointer, is the index needing to be dereferenced first -- something like one of the following:


It would be neither of those. You'd do this:

1
2
cout << (*array)[5] << endl;  // don't leave out the parenthesis
cout << array->operator [] (5) << endl;  // alternative / ugly  


Although -- you're probably better off not doing new Array<int> like that. It's better to just put the array on the stack like your previous example.
I agree, the array->operator [] (5) is very ugly... but interesting.

Okay. Progress. I have cleaned up my code and now all of the previously posted errors are gone, but now the linker is complaining.

I thought perhaps the Array class was conflicting with something else, so I renamed it MyArray and tried again. It yielded the same results.

So, can anyone think of why this is problematic?

I've tried both of the following:

g++ -c array.cpp (success)
g++ test.cpp array.o (fails with the errors below)

and I've tried:

g++ test.cpp array.cpp (which yields the same results (as it should))

The errors follow:

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
/tmp/ccYYewgx.o: In function `main':
test.cpp:(.text+0x70): undefined reference to `Array<int>::Array()'
test.cpp:(.text+0x8f): undefined reference to `Array<int>::Array()'
test.cpp:(.text+0xdc): undefined reference to `Array<int>::Size(unsigned int)'
test.cpp:(.text+0x129): undefined reference to
`Array<int>::operator[](unsigned int)'
test.cpp:(.text+0x189): undefined reference to `Array<int>::Add(int const&)'
test.cpp:(.text+0x19d): undefined reference to `Array<int>::Add(int const&)'
test.cpp:(.text+0x1b1): undefined reference to `Array<int>::Add(int const&)'
test.cpp:(.text+0x1c8): undefined reference to `Array<int>::Delete(unsigned
int)'
test.cpp:(.text+0x1d1): undefined reference to `Array<int>::Size()'
test.cpp:(.text+0x1dd): undefined reference to `Array<int>::Delete(unsigned
int)'
test.cpp:(.text+0x1fb): undefined reference to
`Array<int>::operator[](unsigned int)'
test.cpp:(.text+0x20c): undefined reference to
`Array<int>::operator[](unsigned int)'
test.cpp:(.text+0x223): undefined reference to
`Array<int>::operator[](unsigned int)'
test.cpp:(.text+0x234): undefined reference to
`Array<int>::operator[](unsigned int)'
test.cpp:(.text+0x246): undefined reference to
`Array<int>::operator[](unsigned int)'
/tmp/ccYYewgx.o:test.cpp:(.text+0x259): more undefined references to
`Array<int>::operator[](unsigned int)' follow
/tmp/ccYYewgx.o: In function `main':
test.cpp:(.text+0x277): undefined reference to `Array<int>::Size()'
test.cpp:(.text+0x297): undefined reference to `Array<int>::Clear()'
test.cpp:(.text+0x2b5): undefined reference to `Array<int>::Add(int const&)'
test.cpp:(.text+0x2d3): undefined reference to `Array<int>::Size()'
test.cpp:(.text+0x304): undefined reference to `Array<int>::Size()'
test.cpp:(.text+0x374): undefined reference to
`Array<int>::operator[](unsigned int)'
test.cpp:(.text+0x3c0): undefined reference to
`Array<int>::operator[](unsigned int)'
test.cpp:(.text+0x418): undefined reference to `Array<int>::~Array()'
test.cpp:(.text+0x439): undefined reference to `Array<int>::~Array()'
/tmp/ccYYewgx.o: In function `void output_array<int>(Array<int>*)':
 test.cpp:(.text._Z12output_arrayIiEvP5ArrayIT_E[void
output_array<int>(Array<int>*)]+0x1d): undefined reference to `Array<int>::operator[](unsigned int)'
test.cpp:(.text._Z12output_arrayIiEvP5ArrayIT_E[void
output_array<int>(Array<int>*)]+0x43): undefined reference to `Array<int>::Size()'
collect2: ld returned 1 exit status

Last edited on
The declaration and definition of template functions can't be in two different files.
Okay, that did the trick. Combining of the array.h and array.cpp files into one file. I also took the template output function and moved it into the array class as it didn't really fit as a stand-alone template function anyways.

So, I must say, I've learned a lot thus far. I am quite disappointed though on a few points. Mainly the combining of the header and source files. When did they make that manditory instead of optional? Isn't there any work around? I'd imagine if I could find the C++ headers on the linux box here, that I'd not find all the internal working class code for the streams and such... or would I???
Well, for templates, there is no way to get around it (unless you have a compiler that supports the export which none that I know of do).

As for the source, yeah, you would.
@valen:

If you are interested at this point in learning about exception safety, you should know
that you have many exception-safe problems in your Array class.

One is the implementation of

template <class T>
Array<T>& Array<T>::operator = (const Array &array)

Another is

template <class T>
void Array<T>::Delete(unsigned int pos)
My program has several problems that I can easily see.

Putting aside being redundant with and inferior to the vector class, I'm sure I do need to put in more exception handling. Besides even this, I would need to try to get rid of the C library calls to memcpy etc. if possible.

Since I'm not all that serious about keeping it as a working code, just as a test class, I'm not sure how much more effort will go into it.

All this being said, I appreciate the . It has helped a lot, and I'm still tinkering with what I've learned.

I really do appreciate it greatly. Thanks a ton!
Topic archived. No new replies allowed.