Overloading Square Bracket [] Operator for Class Constructor Array Output in Main

Pages: 12
Hello everybody, I've got a question about overloading brackets.

I need to output the contents of an array, that I created and populated in the constructer of my class, using the [] operator, and I need "to overload the [] in the class."

Our instructions are,
"Write a class that creates and populates an array of integers (any size) using a pointer. Make sure the constructor initializes and populates the array. In your main program output the contents of an object of this class by using the [] operator. You will have to overload the [] in the class. Also, show that you cannot output an array element that is out of bounds."

So far, I've got:
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
#include <iostream>
using namespace std;

class intArray
{
public:

intArray operator [] (intArray); //OVERLOADING [] OPERATOR

intArray(int *pA)
{
pA = new int[10];


int count, j;
j = 0;
for (count = 0; j < 10; j++)
{
pA[j] = 0;
}//end for

//POPULATE ARRAY:
int populate = 5;
for (count = 0; j < 10; j++)
{
pA[j] = populate;
populate++;
}				//end 2nd for
}				//end constructor definition
intArray () { };//"necessary cuz I 'explicitly'...
//...declared another constructor"

};				//end class

int main()
{

intArray mainArray(int *p);
cout << p[0] << endl;//This is the line I'm confused about...

return 0;
}


I know some post on here are desperate last munite attempts to get a program written for some required computer class, but I'm not here for that. CS is my major and I've just got an honest question. =]

Right now my understanding of overloading is so that you can use operators in a slightly different way than normal. So in my case, I guess that I'm using brackets differently because I'm outputting an array in a class constructor that I declared using pointers... I think...

As I commented on line 41 (the 4th-to last line) in my code, I'm stuck there. When I compile, I only get 1 error and it's on that line:

intArray.cpp: In function `int main()':
intArray.cpp:41: `p' undeclared (first use this function)
intArray.cpp:41: (Each undeclared identifier is reported only once for each
function it appears in.)

I know it seems like I'm only outputting the first cell of the array by writting p[0] but I just haven't written the rest of them yet, so I'm just doing p[0] for a test...

So, my big question is: How would I output the array for the object mainArray using the overloaded [] operator?
Provided that your class is written well, I assume that main should look like this:
1
2
3
4
5
int main() {
   int values[] = {1, 2, 3};
   intArray mainArray(values, 3);
   std::cout << mainArray[0];
}


The constructor should allocate an array and copy values from the pointer argument.
Operator [] should be declared as int operator[](int i); and it should return ith element in the array you allocated.
Hi
A simple version of your class

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
class intArray{

private :

	int* pA;
	unsigned int n;

public:

	intArray(unsigned int n ): pA( new int[n] ), n(n){}
	~intArray(){ delete [] pA, n =  0 ;}

	void populate(int p) {
		for (unsigned int  i = 0; i < n ; i++) {
			pA[i] = p;
			p++;
		}

	}

	int& operator[](unsigned int i){ return pA[i];}
	const int& operator[](unsigned int i)const{ return pA[i];}

	int& operator()(unsigned int i){ return pA[i];}
	const int& operator()(unsigned int i)const{ return pA[i];}

	unsigned int size(){ return n;}
	unsigned int size()const { return n;}

};



and main
1
2
3
4
5
6
7
8
9
10
int main() {

	intArray p(10);
	p.populate(5);
	for(unsigned int i = 0 ; i < p.size(); i++)
		cout<<p[i]<<endl;

	return 0;

}
Last edited on
@therockon7throw, you seem to have missed some of the task.
Make sure the constructor initializes and populates the array
and
show that you cannot output an array element that is out of bounds
, unless it was not your goal do do it all..

Your code has more slight problems though. There is no point in having operators [] and () that do the same. There is no point in returning an const int& as returning int would have the same result, but without an extra level of indirection. There is no point in having two size methods as neither of them can modify *this (both are const). Furthermore, I think the compiler would throw an error in line 5 of main() as it would not know which size() to use.
Last edited on
@hamsterman
said
Make sure the constructor initializes and populates the array and show that you cannot output an array element that is out of bounds , unless it was not your goal do do it all..

shall I do the all job for him ? .... :-)

Your code has more slight problems though. There is no point in having operators [] and () that do the same.

no comment :-) do it the way you wish... Sean Walker can learn from it


There is no point in returning an const int& as returning int would have the same result, but without an extra level of indirection.

It can be done that way too, you are not wrong :-)

There is no point in having two size methods as neither of them can modify *this (both are const)

Learn a little more about C++

I think the compiler would throw an error in line 5 of main() as it would not know which size() to use.

The Compiler knows it exactly and I know do :-), but you dont :- )
If the compiler do what you claim I'll wont reply any more, but if your compiler does not than stop replying any more :-)
Last edited on
@therockon7throw, you're being needlessly offensive, don't you think?

Learn a little more about C++
I'd love to see a scenario where the first size() modifies *this.

I admit making a mistake about the error. That was silly of me. It does not make having two methods where one is needed less wrong though.
Last edited on
Okay, I changed it up a bit:

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

//Sean Walker: Project 3 - C++ Data Structures
#include <iostream>

using namespace std;

class intArray
{
public:
intArray()
{
int list[10];
int *pA;
pA = list;
*(pA+0)  = 32;
*(pA+1)  = 35;
*(pA+2)  = 54;
*(pA+3)  = 31;
*(pA+4)  = 90;
*(pA+5)  = 41;
*(pA+6)  = 56;
*(pA+7)  = 98;
*(pA+8)  = 99;
*(pA+9)  = 21;
}

int &operator[](int i);
};

// Provide range checking for intArray.
int &intArray::operator[](int i)
{
if(i<0 || i> 9) {
cout << "Boundary Error\n";
exit(1);
}
return list[i];
}


int main()
{
intArray mainArray;
cout << mainArray[0] << endl;

return 0;
}


when that runs i get

intArray.cpp: In member function `int& intArray::operator[](int)':
intArray.cpp:36: `list' undeclared (first use this function)
intArray.cpp:36: (Each undeclared identifier is reported only once for each
function it appears in.)

so i guess for some reason it doesn't remember me declaring list... I'm %99.9 sure that this is correct, I think there's just one minor thing in there that I'm doing wrong, or maybe more...
I'd love to see a scenario where the first size() modifies *this.
If there were only the first of each then the following would be invalid without const casting away....
1
2
3
4
5
6
 void somefunc (const ArrayList & array){
//.....
array.size (); // error
array [0]; //error
//...
}

Last edited on
@Sean Walker, both list and pA are local variables that don't exist outside intArray constructor. You need a member pointer variable that you could use in all methods. Note that the whole list[] is local, so you can't access its elements outside that constructor. You need to move pA into class scope and use dynamic allocation in the constructor.

@clanmjc, if there was only the second size(), you'd have no problems at all.
Regarding therockon7throw's class:

1. I agree with hamsterman about returning an int rather than a const int& for the const operator[].

2. The non-const size() should be lost. There's absolutely no point in it as the method does not change the internal state of the class. I think a modern compiler would just throw it away as a duplicate.

3. And the operator() methods should be removed as they are invalid semantically. operator[] is for array element access in C++, whereas operator() is for function invocation. Overloading operator() to behave like operator[] breaks the "Preserve the natural semantics of operators" rule (Sutter and Alexandrescu). The stl vector does provide an alternative to operator[], but it's the function at(), which is tidier to use through a pointer.
Last edited on
3_ at() is not equivalent to operator[]
Also you can't have several arguments with [].

2_ Too bad that you can't simplify in the case of []. The algorithm is exactly the same, but you ought to duplicate it.

1_ ¿What do you think that std::vector<int> does? *
Maybe it should be the compiler who decides if pass by value or const reference.

*
1
2
class vector{
  typedef allocator::const_reference const_reference;
So you could especialize the template for every basic type...

Edit: c++11 seems to have changed that for typedef value_type& reference;
Last edited on
Hi

some example from Expression and normal Template C++ Linear Algebra library that implement
both const and normal version of many member-methods;

1-) eigen

1
2
3
 template<unsigned int UpLo> typename SelfAdjointViewReturnType<UpLo>::Type selfadjointView();

    template<unsigned int UpLo> typename ConstSelfAdjointViewReturnType<UpLo>::Type selfadjointView() const
;

1
2
3
template<unsigned int Mode> typename TriangularViewReturnType<Mode>::Type triangularView();

    template<unsigned int Mode> typename ConstTriangularViewReturnType<Mode>::Type triangularView() const;



1
2
3
typename MatrixBase::template DiagonalIndexReturnType<Dynamic>::Type diagonal(Index index);

    typename MatrixBase::template ConstDiagonalIndexReturnType<Dynamic>::Type diagonal(Index index) const;



2- ) Blitz

1
2
3
4
5
6
7
8
9
T& operator()( unsigned int i, unsigned int j)
	{
		return p_Array->operator()(i,j);
	}

	const T& operator()( unsigned int i, unsigned int j)const
	{
		return p_Array->operator()(i,j);
	}

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
unsigned int col()
	{
		return p_Array->col();
	}

	unsigned int row()
	{
		return p_Array->row();
	}

	unsigned int size()
	{
		return col() *  row();
	}

	const unsigned int col()const
	{
		return p_Array->col();
	}

	const unsigned int row()const
	{
		return p_Array->row();
	}

	const unsigned int size()const
	{
		return col() *  row();
	}


3-) HASEM

1
2
T* operator[](int i)const;
	T* operator[](int i);


1
2
3
4
INLINE T maxOfRow(int nRow);
INLINE T maxOfRow(int nRow)const;
INLINE T maxOfCol(int nCol);
INLINE T maxOfCol(int nCol)const;


1
2
3
4
5
6
7
8
9
10
INLINE bool isSymmetric();
INLINE bool isSymmetric()const;
INLINE bool isSkewSymmetric();
INLINE bool isSkewSymmetric()const;
INLINE bool isInvertible()const;
INLINE bool isInvertible();
INLINE bool isNonSingular()const;
INLINE bool isNonSingular();
INLINE bool isSingular()const;
INLINE bool isSingular();


4-) Armadillo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
inline            subview_row<eT> operator()(const uword row_num, const span& col_span);
  inline      const subview_row<eT> operator()(const uword row_num, const span& col_span) const;
  
  
  arma_inline       subview_col<eT> col(const uword col_num);
  arma_inline const subview_col<eT> col(const uword col_num) const;
  
  inline            subview_col<eT> operator()(const span& row_span, const uword col_num);
  inline      const subview_col<eT> operator()(const span& row_span, const uword col_num) const;
  
  inline            Col<eT>  unsafe_col(const uword col_num);
  inline      const Col<eT>  unsafe_col(const uword col_num) const;
  
  
  arma_inline       subview<eT> rows(const uword in_row1, const uword in_row2);
  arma_inline const subview<eT> rows(const uword in_row1, const uword in_row2) const;



Do you need more examples ? Have you any idea why have they implemented both version, not just the const version of the methods ??

andywestken said
The non-const size() should be lost. There's absolutely no point in it as the method does not change the internal state of the class. I think a modern compiler would just throw it away as a duplicate.

Have they all done the same mistake( by implementing both version of methods) ?
Are they all wrong, but yor are right ??

andywestken said
I agree with hamsterman about returning an int rather than a const int& for the const operator[].

See also const T& in Blitz implementation of const T& operator()( unsigned int i, unsigned int j)const ?

andywestken said
And the operator() methods should be removed as they are invalid semantically. operator[] is for array element access in C++, whereas operator() is for function invocation.

one more Question, have you any idea why does Blitz have a implementation for T& operator()( unsigned int i, unsigned int j) ?
Last edited on
Have you any idea why have they implemented both version, not just the const version of the methods ??
No I don't. Do share your knowledge.

Though, const T& is an entirely different case that I never argued against.
if there was only the second size(), you'd have no problems at all
touché
@hamsterman

hamsterman said today
Though, const T& is an entirely different case that I never argued against.


hamsterman said on Feb 18, 2012 at 11:09am
There is no point in returning an const int& as returning int would have the same result, but without an extra level of indirection.


does some one else use your account here ? :-)
Last edited on
@therockon7throw
There is very little overhead with an int so there is no point to pass by reference. The same cannot be said for a template, it could be an int or it could be a complex object.
Hi
@naraku9333

The same cannot be said for a template, it could be an int or it could be a complex object.



does the function const T& operator()( unsigned int i, unsigned int j)const return an object ?

No, so what are you pointing here, is irrelevant. We are not talking about returning an object even if it was an object, it should still be return as a const one( const reference), because you are not returning a temporary object, but an existing/living object or an exisiting and living(member) (even if it is a type of primary such as int , double, float or whatever). So what is the point to return by value?

So what is the advantage of returning by value over returning a const reference?

If the object or the variable(member) is not a temporary one, I use always return by reference it might be const referece or just a reference depending on what are you doing with the object, or what kind object is the source object.(a kind of const or not)

When you use return by value the compiler must create a temporary object/variable and copy the source object/varible and than send the new created object/varibale to where the fucntion was called, and finally destroy the created object after it was copied by caller.

But when you use return by reference no new temporary object/variable is created, not extra copy operation, no need to destroy....


Last edited on
ok, heres the total working complete code that satisfies all of the requirements you listed above:
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
#include <iostream>
#include <cstdlib>
int list[10];

using namespace std;

class intArray
{
public:
intArray()
{
int *pA;
pA = list;
*(pA+0)  = 32;
*(pA+1)  = 35;
*(pA+2)  = 54;
*(pA+3)  = 31;
*(pA+4)  = 90;
*(pA+5)  = 41;
*(pA+6)  = 56;
*(pA+7)  = 98;
*(pA+8)  = 99;
*(pA+9)  = 21;
}

int &operator[](int i);
};

// Provide range checking for intArray.
int &intArray::operator[](int i)
{
if(i<0 || i> 9) {
cout << "Boundary Error\n";
system ("pause");
exit(1);
}
return list[i];
}


int main()
{
intArray mainArray;
int i=0;
cout<<"type the index of the array element you want to see or 12345 to stop the program"<<endl;
while (i!=12345)
{
    cin>>i;
    cout << mainArray[i] << endl;
}
system ("pause");
return 0;
}
@therockon7throw, about const int&, it's great if you have your principles. That doesn't make it better than just int as the compiler will generate the same code any way. I could nitpick at your wording, but that doesn't matter, I'm sure you were exaggerating.
The important thing is that you never said why size() needs two versions. Please do.

@viliml, that's not right. Try creating two array with different values in them. What you need is a member variable. Maybe you should see http://cplusplus.com/doc/tutorial/classes/
Other problems. *(pA+0) is equivalent to pA[0]. The latter should be preferred as it is less hideous.
In your loop you first ask for i, then get the i'th element and only then check if i is 12345. Unless mainArray had 12346 values in it, you're going to exit due to out of bounds error, not the loop condition.
Last edited on
@therockon7throw
I never said there was an advantage or disadvantage with either. My point was that returning an int& is unnecessary with primitive types since a copy is pretty inexpensive. You seem to have the impression that hamsterman and myself are saying not to return by reference, and that is NOT what we are saying.
Pages: 12