Overloaded assignment operator for class template objects

Pages: 12
I designed a class template to create unique arrays. I was able to successfully input data to and output data from my array objects, irrespective of the datatype. However, I can't for the life of me fathom why my overloaded assignment operator worked perfectly well only for integer datatype and not for double/string datatypes.


Here is the class definition:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
template <class dataType>          
class myArray
{
public:
    void setArrayData();
    void print() const;
    dataType& operator[](int index);                    
    const dataType& operator[](int index) const;       
    const myArray& operator=(const myArray&);           
    myArray(int lowerBound = 0, int upperBound = 0);    
    myArray(const myArray& rightArray);                 
    ~myArray();                                         
              
private:
    int arrayLowerBound;
    int arrayUpperBound;
    dataType* arrayPtr;
};



And here is the definition of the overloaded assignment operator:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template<class dataType>
const myArray<dataType>& myArray<dataType>::operator=(const myArray<dataType>& rightArray) 
{
     int i;
    
     if(this != &rightArray)
     {
          delete [] arrayPtr;
          arrayLowerBound = rightArray.arrayLowerBound;
          arrayUpperBound = rightArray.arrayUpperBound;
          arrayPtr = new dataType[arrayUpperBound - arrayLowerBound];
          assert(arrayPtr != NULL);
          for (i = arrayLowerBound; i < arrayUpperBound; i++)
              arrayPtr[i] = rightArray.arrayPtr[i];
     }
     return *this;
}



And here is my main function that tests the operations on objects of the 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
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
int main()
{    
        //object declarations
    myArray<double> list(5);    //a single-parameter object declaration of class myArray
    myArray<double> myList(2,13);    //a two-parameter object declaration of class myArray
    myArray<double> yourList(-5,9);   //----------------------- ditto ------------------------
    myArray<double> hisList(-1,8);    //----------------------- ditto ------------------------
    myArray<double> herList; 
   
    cout<<showpoint<<fixed<<setprecision(2);
    
 
        //I/O for different class objects
    list.setArrayData();
    cout<<endl;
    cout<<"Original list, list";
    list.print();
    cout<<endl<<endl<<endl;
   
    myList.setArrayData();
    cout<<endl;
    cout<<"Original myList, myList";
    myList.print();
    cout<<endl<<endl<<endl;
  
    yourList.setArrayData();
    cout<<endl;
    cout<<"Original yourList, yourList";
    yourList.print();
    cout<<endl<<endl<<endl;
     
    hisList.setArrayData();
    cout<<endl;
    cout<<"Original hisList, hisList";
    hisList.print();
    cout<<endl<<endl<<endl<<endl;
    
  
       //testing the overloaded assignment operator
    myList = list;
    cout<<"After assigning list to myList, myList";
    myList.print();
    cout<<endl<<endl;
  
    myList = yourList;
    cout<<"After assigning yourList to myList, myList";
    myList.print();
    cout<<endl<<endl;  
  
    list = hisList;
    cout<<"After assigning hisList to list, list";
    list.print();
    cout<<endl<<endl;
  
    yourList = hisList;
    cout<<"After assigning hisList to yourList, yourList";
    yourList.print();
    cout<<endl<<endl<<endl<<endl;  
   
    
    // wait until user is ready before terminating program
    system("PAUSE");
    return 0;
}  


The problem I'm having starts from where the assignment operator is being tested: for double and string datatypes, the upper input/output section works fine, but the assignment section freezes the display until the program execution is manually terminated!!

Any helpful insights would be highly appreciated. Please feel free to ask or request for clarifying info about the code. Thanks as I eagerly await your responses.
Last edited on
1
2
3
arrayPtr = new dataType[arrayUpperBound - arrayLowerBound];
/*...*/
for (i = arrayLowerBound; i < arrayUpperBound; i++)
What if lower bound is 9 and upper bound is 100? You will create an array of size 1, but will try to access 99th element.
Also problem with negative elements. Remember: arratPtr[i] will not use overloaded operator[].
If it works for ints, I think you have some problems with operator[] too. Show it.
Thanks MiiNiPaa for your prompt reply. Let me try to respond:

You wrote: "What if lower bound is 9 and upper bound is 100? You will create an array of size 1, but will try to access 99th element."
Did you mean to write 10 and not 100? 'Cause that's the only way the array size would result in 1.

Also you wrote: "Also problem with negative elements." Did you mean negative elements of the array OR negative parameters at object declaration? Anyways, the index of arrayPtr (= i) can never be negative, given some object declaration constraint (e.g. upperBound>lowerBound) and the way I wrote the constructor.

The constructor is definitely not the problem; like I implied in my earlier post, the objects of the class were successfully created (for integer, double, and string datatypes); it's the assignment operation that's failing me, I'm guessing. Here are my constructor and the overloaded operator[] you asked for:



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
template<class dataType>
myArray<dataType>::myArray(int lowerBound, int upperBound)
{
    int arraySize;
    
    if (upperBound == 0)                   
    {                                    
        arrayLowerBound = 0;                   
        arrayUpperBound = lowerBound;
    }
    else                                    
    {
        arrayLowerBound = lowerBound;
        arrayUpperBound = upperBound;
    }
    
    arraySize = arrayUpperBound - arrayLowerBound;
    arrayPtr = new dataType[arraySize];
}     



template<class dataType>
dataType& myArray<dataType>::operator[](int index)
{
     if (arrayLowerBound <= index && index < arrayUpperBound)
         return arrayPtr[index];
     else
     {
         cout<<"Array index out of bound!! Bailing out!"<<endl<<endl<<endl;
         
         // wait until user is ready before terminating program
         system("PAUSE");
         abort();        //terminates program due to erroneous array index
     }       
     
     assert(arrayLowerBound <= index && index < arrayUpperBound);   
     return arrayPtr[index];                                         
}



By the way, I'm jealous of the way you captured the exact fonts/color of my program segment. Yours even had line numbers! So cool! How do I go about doing that on here; I'm fairly new to the forum. Thanks.


Last edited on
There is code tags button. It looks like <>.

Did you mean to write 10 and not 100?
I meant 99 :)

In your constructor: if you pass (-10, 0) as parameters, it will create array of size 10 with valid indexes [0,9]
But if you call yoir object as x[-8] it will chech that it is between lower and upper bound (which is true) and will try to do arrayPtr[-8];, which is dangerous and can lead to crash.
You probably intended to normalize your range and access underlying array as arrayPtr[index - lowerBound]
What is the problem with the copy assignment operator? I would decalre it without qualifier const in the return type

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
template<class dataType>

myArray<dataType> & 
myArray<dataType>::operator =( const myArray<dataType> &rightArray ) 
{
   if ( this != &rightArray )
   {
      dataType *newPtr = new dataType[rightArray.arrayUpperBound - rightArray.arrayLowerBound];
      assert( newPtr != NULL );

      std::copy( rightArray.arrayPtr, 
                 rightArray.arrayPtr +  rightArrat.arrayUpperBound - rightArray.arrayLowerBound, 
                 newPtr );

      delete [] arrayPtr;

      arrayLowerBound = rightArray.arrayLowerBound;
      arrayUpperBound = rightArray.arrayUpperBound;
      arrayPtr = newPtr;
   }
 
   return *this;
}



As for me I would make arrayUpperBound acceptable to specify as an index.
Last edited on
closed account (o1vk4iN6)
I wouldn't use copy, I'd use move instead such that if the object has a copy constructor for a rvalue reference then you will be saying time instead of allocating extra memory in the copy.

1
2
3
      std::move( rightArray.arrayPtr, 
                 rightArray.arrayPtr +  (rightArrat.arrayUpperBound - rightArray.arrayLowerBound), // avoid doing unnecessary multiplication of type size
                 newPtr );
I wouldn't use copy, I'd use move instead


Logically that wouldn't make sense. This is copy assignment, not move assignment. The elements to be moved are const and thus cannot be "moved" as doing so requires the source elements to be modified.


[edit: typo'd]
Last edited on

@MiiniPaa: I see your point about the dangers of problematic "extreme" parameters during object declaration. However, the point still remains that I did not use those extreme parameter cases in my first post above that worked for integer datatypes but failed for double/string datatypes. I have already designed a more versatile constructor to take care of those extreme parameters at object declarations. It just boggles the mind that the same set of parameters that worked for integers would not work for double/string datatypes.

@vlad from moscow: Let's assume the "const" in the return type were the problem, how come this did not affect the integer datatype runs I did, all of whose assignment operations were successfully implemented??

@xerzi: Well, the "Law of the Big Three" stipulates inclusion of the copy constructor in a class with pointer data members.

@cire: I think I agree with your objection to xerzi's post.


Still looking forward to any helpful insights. Thank you all.


I did not know what is your problem with the copy assignment operator because you did not say nothing about it.
@vlad from moscow:
I did not know what is your problem with the copy assignment operator because you did not say nothing about it.

If you examine my first post in which I show function main, you would see four assignment statements in lines 40, 45, 50 & 55. These are the lines that are giving me a headache. Like I said in my first post, these assignment statements are successfully achieved when the objects are of integer datatype. However, program execution halts if the objects are of double/string datatypes. That's why it makes sense to think that my overloaded assignment statement is the problem. But from a theoretical standpoint, I can't see where my overloaded assignment operator function is wrong.


@cire: I'll try to go over that website you suggested.

Thanks all! The search for a reason for this datatype-based execution anomaly continues.
I would guess that, since every function you've shown so far has handled indexing your array incorrectly (ie: not adjusting the index to be zero-based before using it to access the array), by the time you get to the later tests you've been steadily trashing memory and the results of that finally show up.

@cire:
You wrote:
I would guess that, since every function you've shown so far has handled indexing your array incorrectly (ie: not adjusting the index to be zero-based before using it to access the array), ...


Well that's the point of the task I embarked upon: to create non-conventional (not necessarily zero-based) arrays that are strictly based on the definition of the class to which they belong. I beg to differ that the indexing is being handled incorrectly because my overloaded array index operator function(definition shown in an earlier post) has already taken care of the non-zero-based feature of the objects of the class.
Read my posts again.
If you create myArray<double> x(99, 100) it will create array with one element.
If you do x[99] it will pass your checks, but will access 100th one which is overflow

If you create myArray<double> y(-10, 0) it will create array with ten elements.
If you do y[-8] it will pass your checks, but will access -8th element, which is invalid for arrays.

Look what your function doing in these cases step by step and you will probably understand that yourself
@MiiNiPaa & @cire:

I think I'm starting to see your points about the array indexing angle to my problem. For some reason, I have apparently and erroneously overlooked the fact that within the function definition of the operator I'm trying to overload, the primary ANSI C++ definition of that same operator is still preserved.

MiiNiPaa's examples sort of illustrate this: in main function, given the nature of my class, it makes sense to have y[-8] (i.e. trying to access an array component of object y); however, within the overloaded operator function's definition for [], this array component reference is gibberish!!

I'll go and explore this dimension to the problem and let you guys know if it resolves it. In the meantime, thanks.
MiiNiPaa wrote:
You probably intended to normalize your range and access underlying array as arrayPtr[index - lowerBound]
@MiiNiPaa:

Oh yeah! Although I had already come up with arrayPtr[index + (-1*arrayLowerBound)], essentially the same as yours. It's funny how even though I read all your prior posts, the epiphany I had on the indexing pitfall of my code did not occur until my last post. I observed you caught this pretty early on!

After making the necessary adjustments, I observed a marginal improvement in program behavior; however, I now see some strange, random, large numbers in the assignment section that I did not input in the first place! This aberrant behavior even for integer datatypes that I thought were in the clear before. Of course, the object input/output section for all datatypes still works fine; the assignment section of the code is where things go awry.

I have included a sample program output here for your observation:




Enter 5 data items for a myArray object: 1 2 3 4 5

Original list, list(5) = 1 2 3 4 5


Enter 11 data items for a myArray object: -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11

Original myList, myList(2,13) = -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11


Enter 14 data items for a myArray object: 10 20 30 40 50 60 70 80 90 100 110 120 130 140

Original yourList, yourList(-5,9) = 10 20 30 40 50 60 70 80 90 100 110 120 130 140


Enter 9 data items for a myArray object: 31 32 33 34 35 36 37 38 39

Original hisList, hisList(-1,8) = 31 32 33 34 35 36 37 38 39



After assigning list to myList, myList(5) = 1 2 3 4 5

After assigning yourList to myList, myList(-5,9) = 10 20 30 40 50 778333539 897346723 20236 3674192
3670212 3 1819233116 1065184424 134237963

After assigning hisList to list, list(-1,8) = 31 32 33 34 35 778333539 863857828 201346828 10

After assigning hisList to yourList, yourList(-1,8) = 31 32 33 34 35 36 37 38 39



Press any key to continue . . .



Any insights?
1
2
          for (i = arrayLowerBound; i < arrayUpperBound; i++)
              arrayPtr[i] = rightArray.arrayPtr[i];

If that wasn't changed, you got same problem here. Either use your array subscript operator to handle this, or corrext indexes manually.
Like I said, I already made all the adjustments related to the array indexing throughout my class' member functions.
Show how your assigment operator looks now.
Pages: 12