Constructos with Dynamic Allocation

Pages: 12
I'm making a class that dynamically creates arrays. I'm having a lot of trouble with the constructors.

I'm trying to make a constructor that assigns a value in its parameter, and it only carries out the assignment if the constructor has no arguments passed to it. No matter how I set up the prototype and implementation, however, the program either fails to compile or successfully compiles but never carries out the assignment.

Here's my code (trimmed down 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
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
#ifndef Array_h
#define Array_h

#include<iostream>
#include <cstdlib>
#include <iomanip>
using namespace std;

template <typename T>

class Array
{
     public:

//Array(T const& value = T());


        friend ostream & operator << (ostream& os, const Array<T> & out)
        {
                for (int i = 0; i < out.size; i++)
                {
                        os << out.arr[i];
                }
                return (os);
        }

        friend istream & operator >> (istream & is, Array<T> & in)
        {
                int count = 0;

                for (int i = 0; i < in.size; i--)
                {
                        cin >> in.arr[i];
                }
                return (is);
        }

        Array(int s); // default constructor
        Array(const Array &); // copy constructor

        ~Array(); // destructor

        int getsize() const; // return size

        // assignment operator
        const Array & operator= (const Array &);

        //equality operator
        bool operator == (const Array &) const;

        // inequality operator; returns opposite of == operator
        bool operator != (const Array &) const
        {
                return ! (*this == right); //invokes Array::operator==
        } // end function operator !-
  static int getDELIM()
        {
                return(DELIM);
        }

        T & operator[] (int);

        T operator [] (int) const;

    private:
        static const int DELIM = -999; // example of using a delimiter to signal end of input
        static const int DEFAULTSIZE = 10; // default array size

        int size;
        T *arr;
};
#endif

template<class T> Array<T>::Array(int s = DEFAULTSIZE)
{
        if(s != DEFAULTSIZE)
                size = s;

        arr = new T [size];

        if(!arr)
        {
                delete [] arr;
                cerr << "error\n";
                exit(EXIT_FAILURE);
        }
}

template<class T>  Array<T>::Array(const Array &copy)
{
        arr = copy.arr;
}

template<class T> Array<T>::~Array()
{
        delete [] arr;
        arr = NULL;
}
Default Parameters should be only in the function prototype, for example if i consider the folllowing function

//Prototype
void func(int x=0, int y=0);

so the function definition will be
void func(int x, int y)
{

int a=x;
char b=(char)y;

}
You're attempting to supply the default size on the definition. It must be provided on the declaration.

Line 38:
 
Array(int s = DEFAULTSIZE); // default constructor 


Line 74:
 
template<class T> Array<T>::Array(int s)


Line 76 should be omitted.
It is better and easier to define templated classes completely inline.
Ahh

I had tried that earlier, and when I received an error I assumed it was because of the syntax.

But since you just told me it is, indeed, the correct syntax, I looked elsewhere for the error, and found it in my .cpp file. Thanks for the help!


I also have another question, regarding copy constructors.

From what I read online, the purpose of the copy constructors is to allow you to duplicate all of the pointers and objects of a class from one instance to another instance, since if you don't do that you will have pointers of each object basically pointing to the same thing.

I guess I understand that, but I really don't see where I would use the constructor in my code. Althouh I created a copy constructor in my code, I never used it once in the program and everything works perfectly.

I'm trying to learn how to program in the most efficient way, however, and so I'm trying to learn how to utilize the copy constructor effectively.

So my question is this: where in my code would a copy constructor be beneficial? As of now, I see no reason to ever use it when I can just use the overloaded "=" operator.

This is my new code (I revised my copy constructor from my previous post after doing some online research):

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
#ifndef Array_h
#define Array_h

#include<iostream>
#include <cstdlib>
#include <iomanip>
using namespace std;

template <typename T>

class Array
{
     public:

//Array(T const& value = T());


        friend ostream & operator << (ostream& os, const Array<T> & out)
        {
                for (int i = 0; i < out.size; i++)
                {
                        os << out.arr[i];
                }
                return (os);
        }

        friend istream & operator >> (istream & is, Array<T> & in)
        {
                int count = 0;

                for (int i = 0; i < in.size; i--)
                {
                        cin >> in.arr[i];
                }
                return (is);
        }

        Array(int s = DEFAULTSIZE); // default constructor
        Array(const Array &); // copy constructor

        ~Array(); // destructor

        int getsize() const; // return size

        // assignment operator
        const Array & operator= (const Array &);

        //equality operator

        bool operator == (const Array &) const;

        // inequality operator; returns opposite of == operator
        bool operator != (const Array &) const
        {
                return ! (*this == right); //invokes Array::operator==
        } // end function operator !-

        static int getDELIM()
        {
                return(DELIM);
        }

        int getsize()
        {
                return(size);
        }

        T & operator[] (int);

        T operator [] (int) const;

    private:
        static const int DELIM = -999; // example of using a delimiter to signal end of input
        static const int DEFAULTSIZE = 10; // default array size

        int size;
        T *arr;
};
#endif

template<class T> Array<T>::Array(int s)
{
        size = s;

        arr = new T [size];

        if(!arr)
        {
                delete [] arr;
                cerr << "error\n";
                exit(EXIT_FAILURE);
        }
}

template<class T>  Array<T>::Array(const Array &copy)
{
        arr = new T (*arr.copy);
}

template<class T> Array<T>::~Array()
{
        delete [] arr;
        arr = NULL;
}

template<class T> T & Array<T>::operator[] (int subscript)
{
        if (subscript < 0 || subscript >= size)
        {
                cerr << "Error";
                exit(EXIT_FAILURE);
        }
        return(arr[subscript]);
}

template<class T> T Array<T>::operator[] (int subscript) const
{
        if (subscript < 0 || subscript >= size)
        {
                cerr << "Error";
                exit(EXIT_FAILURE);
        }
        return (arr[subscript]);
}

template<class T> bool Array<T>::operator == (const Array<T> & op2) const
{
        bool arrayEqual = true;

        if(size != op2.size)
                arrayEqual = false;
        else
        {
                for (int i = 0; i < op2.size; i++)
                {
                        if(arr[i] != op2.arr[i])
                                arrayEqual = false;

                        if(!arrayEqual) // If arrayEqual is false once, it is no longer necessary to check and continue the loop
                                i = op2.size;
                }
        }
        return (arrayEqual);
}

template<class T> const Array<T> & Array<T>::operator = (const Array<T> & op2)
{
        if (size >= op2.size)
        {
                size = op2.size;
        }
        else
        {
                delete [] arr;
                size = op2.size;
                arr = new T [size];
        }

        for (int i = 0; i <= op2.size; i++)
        {
                arr[i] = op2.arr[i];
        }
}
Oh, I didn't even see those other two posts before I made my post.

When you say define functions "inline" I assume you mean define them all in the body of class Array {......}; endif

Kind of like I did with the get functions, and so "endif" will be the last line of code in my .h. Is that what you mean by inline?
Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template<typename T>
struct MyClass
{
    void foo()
    {
        //defined inline
    }
    void bar(); //declared, not defined
};

template<typename T> //have to retype this, annoying
void MyClass<T>::bar() //also annoying to type
{
    //definition
}
The other reason is that the definition of bar has to be in the header anyway or you won't be able to call bar because its definition is not visible (you can't put the definition in a cpp file). This is a side-effect of templates - the compiler has to see everything about the template to properly instantiate it.

INeedAHero wrote:
I guess I understand that, but I really don't see where I would use the constructor in my code. Althouh I created a copy constructor in my code, I never used it once in the program and everything works perfectly.
Generally, the copy constructor is called without you noticing:
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
#include <iostream>

struct MyClass
{
    MyClass() //default ctor
    {
        std::cout << "Constructed" << std::endl;
    }
    MyClass(MyClass const&from) //copy ctor
    {
        std::cout << "Copied" << std::endl;
    }
    ~MyClass() //dtor
    {
        std::cout << "Destructed" << std::endl;
    }
};

void f(MyClass) //pass by value
{
    std::cout << "f" << std::endl;
}

int main()
{
    MyClass mc;
    f(mc);
    std::cout << "End of main" << std::endl;
}
Constructed
Copied
f
Destructed
End of main
Destructed
http://ideone.com/JcncId
Last edited on
From what I read online, the purpose of the copy constructors is to allow you to duplicate all of the pointers and objects of a class from one instance to another instance, since if you don't do that you will have pointers of each object basically pointing to the same thing.


The purpose of a copy constructor is to create a copy of an object. Having raw pointers in your class usually means the defaulted copy constructor will do the wrong thing, so it is necessary to write your own.


I guess I understand that, but I really don't see where I would use the constructor in my code.


1
2
3
4
5
6
7
8
Array<T> generateArray() ;

int main()
{
    Array<T> a = generateArray() ; // copy constructor required.
    Array<T> b = a ;  // copy constructor required.
}


Althouh I created a copy constructor in my code, I never used it once in the program and everything works perfectly.


It's probably good you haven't used it. It is wrong.

1
2
3
4
5
6
7
8
9
10
11
template<class T>  Array<T>::Array(const Array &copy)
    : size(copy.size), arr(new T[size])
{
    try {
        std::copy(copy.arr, copy.arr+size, arr) ;
    }
    catch(...) {
         delete [] arr ;
         throw ;
    }
}


1
2
3
4
5
6
7
Array<T> generateArray() ;

int main()
{
    Array<T> a = generateArray() ; // copy constructor required.
    Array<T> b = a ;  // copy constructor required.
}


Yes, I did read online that copy constructors come into play when making assignments.

But in my main.cpp, I assign a small object (size of array == 5) to a large object (size of array == 7), and my overloaded "=" is able to handle the assignement with no trace of an error.

This is the code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Array<int> arr1(5);
        cout << "Enter the values of the array\n";

        for (int i = 0; i < arr1.getsize(); i++)
        {
                cin >> arr1[i];
        }

        Array<int> arr2(7);

        cout << "Enter the values of the array\n";

        for (int i = 0; i < arr2.getsize(); i++)
        {
                cin >> arr2[i];
        }

        cout << "Now, let's assign these two arrays of different sizes. We will assign arr1 = arr2, and arr2 is larger. Let's see what happens!\n";
        arr1 = arr2;
        cout << "Now let's cout the resulting arr1 (which should be identical to arr[2]\n";
        cout << "arr2 = " << arr2 << endl;
        cout << "arr1 = " << arr1 << endl;


And arr2 and arr1 are indeed equal when you input different values into each in the beginning. There's no doubt that my copy constructor is wrong, but it's ugliness seems to never has an impact on my program.

It seems like my "=" function is not doing what an "=" is supposed to do in this specific situation, but even there I don't see what could be wrong.

1
2
    Array<T> a = generateArray() ; // copy constructor required.
    Array<T> b = a ;  // copy constructor required. 


Yes, I did read online that copy constructors come into play when making assignments.

If you read that, it was incorrect. There is no assignment here, only initialization.

1
2
3
4
5
    Array<T> a ;
    Array<T> b = a ;   // copy constructor called, initialization
    Array<T> c (a) ;   // copy constructor called, initialization
    Array<T> d ;       // default constructor called, initialization
    d = a ;            // copy assignment operator called, assignment 
Last edited on
The = is a lie. Many people opt not to use it for construction because it is misleading.
Wow, that was a big help. Now I see the purpose of the copy constructor - it's only use is when you initialize an object as equal to another object. And it's normally done automatically, but with pointers you need to be more careful.

But ene thing that gives me hesitation is this example you showed me:

1
2
3
4
5
6
7
8
9
10
11
template<class T>  Array<T>::Array(const Array &copy)
    : size(copy.size), arr(new T[size])
{
    try {
        std::copy(copy.arr, copy.arr+size, arr) ;
    }
    catch(...) {
         delete [] arr ;
         throw ;
    }
}


I have no idea what line 5 is....is 'copy' just a function that pre-exists in the system? I also assume that the "catch" function just checks that the dynamic allocation was successful?

I'm only asking because this is the first time in my life I've ever made a copy constructor - I worry that although my program now initializes objects to other objects correctly, I still may be doing the task inefficiently.

This is my copy constructor, do you see anything blatantly wrong with it?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 Array(const Array & copier) // copy constructor
        {
                size = copier.size;
                arr = new T [size];

                if(!arr)
                {
                        delete [] arr;
                        cerr << "error\n";
                        exit(EXIT_FAILURE);
                }
                else
                {
                        for (int i = 0; i <= copier.size; i++) //I opt not to use '=' operator becuase '=' operator
                                                               //checks the size, which in this case is not necessary
                        {
                                arr[i] = copier.arr[i];
                        }
                }

        }


Thank you all for your help
I have no idea what line 5 is....is 'copy' just a function that pre-exists in the system? I also assume that the "catch" function just checks that the dynamic allocation was successful?


http://www.cplusplus.com/reference/algorithm/copy/

Copy is a standard algorithm used for copying one sequence to another.

One does not know, necessarily, what one may be storing in an Array. The try/catch is there in case the copy assignment operators invoked by the copy algorithm throw. If they do throw an exception, we need to deconstruct the objects we created with new and free the memory via delete. If new fails, an exception would be thrown that would not be caught in the constructor.

As far as your version of the copy constructor - new will throw an exception if it fails, so there is no chance of the if (!arr) body being entered. The else body will always be executed and if any of those copy assignment invocations throw, then you will leak memory.

Also, size and arr are initialized here:
1
2
3
template<class T>  Array<T>::Array(const Array &copy)
    : size(copy.size), arr(new T[size])
{


And default-initialized here, then assigned to in the body of the constructor:
1
2
3
4
 Array(const Array & copier) // copy constructor
        {
                size = copier.size;
                arr = new T [size];
As far as your version of the copy constructor - new will throw an exception if it fails, so there is no chance of the if (!arr) body being entered. The else body will always be executed and if any of those copy assignment invocations throw, then you will leak memory.


So, may I presume that the if (!arr) body of my other constructor(line 89-92 of the post dated Feb. 26th 10:00pm) would also never be executed?
So, may I presume that the if (!arr) body of my other constructor(line 89-92 of the post dated Feb. 26th 10:00pm) would also never be executed?


Correct.
Thanks for the help.

I also have been looking over my code, and I noticed something that makes me wonder. In my "=" operator, would it be possible / wise to call the copy constructor?

This is my '=' operator function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const Array & operator = (const Array & op2)
        {
                if (size >= op2.size)
                {
                        size = op2.size;
                }
                else
                {
                        delete [] arr;
                        size = op2.size;
                        arr = new T [size];
                }

                for (int i = 0; i <= op2.size; i++)
                {
                        arr[i] = op2.arr[i];
                }

        }


In the else statement, I'm essentially deleting the array, and then creating a new one. I realized that that is exactly what copy constructor does, in a way.

When I tried calling the copy constructor, however, I received an error message. I tried playing around, but I couldn't get it to work, and I don't want to spend too much time on this idea if it isn't practical.

So, in general, would it make sense to call the copy constructor in this kind of situation?
your operator= is ok (you need to return *this by the way)

You find the reason for the crash on line 14:

for (int i = 0; i <= op2.size; i++) // The = makes it go out of bounds

Oh and const Array & operator = (const Array & op2)
The return value is only good for a sequence of assignments (you might even have void)
Last edited on
In my "=" operator, would it be possible / wise to call the copy constructor?

Good question: if you write a few copy constructors and copy assignment operators you will notice that in general, copy assignment will repeat the code from the destructor and then will repeat the code of the copy constructor.
There is a common idiomatic solution to this: the copy-and-swap assignment operator:

1
2
3
4
5
6
Array & operator= (Array op2) // pass by value calls the copy/move ctor
{
    swap(size, op2.size);
    swap(arr, op2.arr);
    return *this;
} // end of scope for op2 calls the destructor 


this operator has other useful properties besides avoiding repeated code, and is the best thing possible in *general case*

However, in your specific case the users of your class are likely to assign Arrays of the same size more often than of varying sizes, so allocating/deallocating on every assignment is wasteful, and your approach with an if-else is the right direction.
The copy constructor takes advantage of optimization by initializing members to the correct values in the first place rather than to default values followed by the correct values. The assignment operator assumes the object has already been used and so you need to overwrite the values.

If you feel like being really hacky, you can call your own destructor and then call placement-new, but I really recommend not doing that. It also makes some assignment operator optimizations like yours impossible, and you would have to check for self assignment.
Ok guys, thanks for the help!
Pages: 12