Array indexing with copy constructor, assignment operator and index operator overload The code below is a custom array class that handles indexing, copying, printing, etc. explicitly.
Is there a better approach for specification (declaration and definition) of the assignment operator (with swap function) and the index [] operator?
Furthermore, are there any improvements that can be made to the other methods to improve functionality?

// Overloading operators for Array class
#include&lt;iostream&gt;
#include&lt;cstdlib&gt;

using namespace std;

// A class to represent an integer array
class Array{
private:
    int *ptr;
    int size;
public:
    Array(int *p = NULL, int s = 0);
    Array(const Array&amp;);
    ~Array();
    Array&amp; operator= (Array);

    // Overloading [] operator to access elements in array style
    int &amp;operator[] (int);

    // Utility function to print contents
    void print() const;

    friend void swap(Array&amp; first, Array&amp; second);};


// Implementation of [] operator.  This function must return a
// reference as array element can be put on left side
int &amp;Array::operator[](int index){   
    if (index &gt;= size || index &lt; 0){
       throw out_of_range("Index out of Range error");
    }
    return ptr[index];
}

// constructor for array class
Array::Array(int *p, int s){
    size = s;
    ptr = NULL;
    if (s != 0){
        ptr = new int[s];
        for (int i = 0; i &lt; s; i++)
            ptr[i] = p[i];}
}

// destructor for array class
Array::~Array(){
    delete[] ptr;
    ptr = NULL;}

// copy constructor for array class
Array::Array(const Array&amp; A) { 
    size = A.size;
    ptr  = new int[size];
    for (int i = 0; i &lt; size; i++)
        ptr[i] = A.ptr[i];}

//swap friend function of assignment operator
void swap(Array&amp; first, Array&amp; second){
    using std::swap;
    swap(first.size, second.size);
    swap(first.ptr, second.ptr);}

//Assignment operator for array class
Array&amp; Array::operator=(Array other){
    swap(*this, other); 
    return *this;}

//print function for array elements
void Array::print() const{
    cout &lt;&lt; "{";
    for(int i = 0; i &lt; size; i++)
        cout&lt;&lt;ptr[i]&lt;&lt;" ";
    cout&lt;&lt;"}"&lt;&lt;endl;}

// Driver program to test above methods
int main()
{
    int a[] = {1, 2, 3, 4, 5, 6};
    Array arr1(a, 6);
    arr1[0] = 7;
    arr1.print();
    Array arr2 = arr1;
    arr2.print();
    arr1[-1] = 4;
    return 0;
}
This isn't really a code review so much as help on how to get this to compile. 


Array::Array(int *p = NULL, int s = 0){ default parameters go on the declaration, not the definition.*
Array::Array&amp; operator=(Array other){ Qualify the operator with Array::, not the return type. You're not returning an Array::Array**, you're defining the operator= member function of Array
void Array::swap(Array&amp; first, Array&amp; second){ swap is not a member of array, it's a friend. Remove the Array::


*This is because the client (caller) to the method needs to know what the defaults are supposed to be, since default parameters are mostly a syntactic shortcut. If you put the defaults into the definition (e.g. a cpp/.o file), a client which includes the header cannot see the definition and thus cannot use them. In other words, there exists no Array::Array() constructor right now, there's only the Array::Array(int *, int), but through C++ magic it can look as though Array::Array() is being called. 

** An Array::Array type is impossible because you can't name a child type the same as its parent type. It would be ambiguous with the constructor.