Конструктор копирования не копирует глубоко, ошибка возвращается с пустым массивом - PullRequest
1 голос
/ 26 апреля 2019

Я делаю проект для школы, и он требует конструктора копирования, деструктора и т. Д. Когда я использую конструктор копирования, выдается сообщение о том, что массив пуст, я предполагаю, что конструктор копирования не работает.

Когда я удаляю конструктор копирования, программа работает, что означает, что проблема, вероятно, возникает в этой функции.

template<class T>
class DynArray {
public:

DynArray<T>(){
 ptr = new T[Capacity];
 this->Capacity = 2;
 this->Size = 0;  
}

DynArray<T>(T n) {
 ptr = new T[Capacity];
 this->Capacity = n;
 this->Size = 0;
}

DynArray<T>(const DynArray& orig) {
 cout << "Copy" << endl;
 ptr = new T[Size];
 *ptr = *(orig.ptr);
}

DynArray<T>& operator=(const DynArray<T>& orig) {
 if(this != &orig) {
 delete[] ptr;
 ptr = new T[Size];
 *ptr = *(orig.ptr);
}
return *this;  
}

void push_back(const T& n) {
 if (Size >= Capacity) {
 adjust(Capacity * 2);
 }
 ptr[Size] = n;
 Size++;
} 

void adjust(T a) {
 cout << "grow" << endl;
 T* arr = new T[a];
 for (int i = 0; i < Capacity; ++i) {
 arr[i] = ptr[i];
}
Capacity = a;
ptr = arr;
}

T& back() {
 if(Size == 0) {
 throw runtime_error("Array is empty");  
}
return ptr[Size - 1];
}

T& front() {
 if(Size == 0) {
 throw runtime_error("Array is empty");  
}
return ptr[0];
}

private:
 T* ptr = nullptr;
 int Capacity;
 int Size;

main:

#include <iostream>
#include "dynarray.h"
using namespace std;

int main( )
{
const char START = 'A';
const int MAX = 12;

// create a vector of chars
DynArray<char> vectD;

// push some values into the vector
for (int i = 0; i < MAX; i++)
{
    vectD.push_back(START + i);
}

// remove the last element
vectD.pop_back();

// add another value
vectD.push_back('Z');

// test memory management
DynArray<char> vectD2 = vectD;
// display the contents
cout << "\n[";
for (int i = 0; i < vectD2.size() - 1; i++)
{
    cout << vectD2.at(i) << ", ";
}

cout << "..., " << vectD2.back() << "]\n";

DynArray<char> vectD3;
vectD3 = vectD2;
cout << "\n[";
for (int i = 0; i < vectD3.size() - 1; i++)
{
    cout << vectD3.at(i) << ", ";
}
cout << "..., " << vectD3.back() << "]\n";

vectD3.front() = '{';
vectD3.back() = '}';
cout << vectD3.front();
for (int i = 1; i < vectD3.size() - 2; i++)
{
    cout << vectD3.at(i) << ", ";
}
cout << vectD3.at(vectD3.size()-2) << vectD3.back() << endl;
}

Позже в моем коде он установленчтобы бросить runtime_error, если Size == 0, он выдает ошибку, означающую, что массив пуст.Правильно ли копирует конструктор копирования?Main нельзя изменить, это дано из проф.

UPDATE : я изменил конструктор копирования, чтобы скопировать все элементы массива, но runtime_error по-прежнему возвращает сообщение о том, чтомассив пуст.

template<class T>
class DynArray {
public:

DynArray<T>(){
 ptr = new T[Capacity];
 this->Capacity = 2;
 this->Size = 0;  
}

DynArray<T>(T n) {
 ptr = new T[Capacity];
 this->Capacity = n;
 this->Size = 0;
}

DynArray<T>(const DynArray& orig) {
 cout << "Copy" << endl;
 ptr = new T[Size];
 for (int i = 0; i < Size; i++) {
 ptr[i] = orig.ptr[i];
}
}

DynArray<T>& operator=(const DynArray<T>& orig) {
 if(this != &orig) {
 delete[] ptr;
 ptr = new T[Size];
 for (int i = 0; i < Size; i++) {
 ptr[i] = orig.ptr[i];
}
}
return *this;  
}

void push_back(const T& n) {
 if (Size >= Capacity) {
 adjust(Capacity * 2);
 }
 ptr[Size] = n;
 Size++;
} 

void adjust(T a) {
 cout << "grow" << endl;
 T* arr = new T[a];
 for (int i = 0; i < Capacity; ++i) {
 arr[i] = ptr[i];
}
Capacity = a;
ptr = arr;
}

T& back() {
 if(Size == 0) {
 throw runtime_error("Array is empty");  
}
return ptr[Size - 1];
}

T& front() {
 if(Size == 0) {
 throw runtime_error("Array is empty");  
}
return ptr[0];
}

private:
 T* ptr = nullptr;
 int Capacity;
 int Size;

Ответы [ 2 ]

1 голос
/ 27 апреля 2019

В вашем классе довольно много ошибок.

Конструктор по умолчанию использует Capacity для выделения массива до инициализации Capacity.

Ваш конструктор резервирования объявленнеправильно.Его входной параметр должен быть int вместо T.И он страдает от той же ошибки Capacity, что и ваш конструктор по умолчанию.

Ваш конструктор копирования также страдает от той же ошибки инициализации, только с Size.И это не выполняет глубокое копирование вообще.Это просто копирование указателя ptr из одного экземпляра класса в другой, независимо от того, на что он указывает.То же самое с вашим оператором присваивания копии.

Ваш метод adjust() также объявлен неверным, и происходит утечка памяти.

Сказав это, попробуйте что-то более похожее на это:

#include <algorithm>
#include <utility>

template<class T>
class DynArray
{
public:

  DynArray(int n = 2)
    : ptr(new T[n]), Capacity(n), Size(0)
  {
  }

  DynArray(const DynArray& orig)
    : DynArray(orig.Size)
  {
    std::cout << "Copy" << std::endl;
    std::copy(orig.ptr, orig.ptr + orig.Size, ptr);
    Size = orig.Size;
  }

  DynArray(DynArray&& orig)
    : ptr(nullptr), Size(0), Capacity(0)
  {
    orig.swap(*this);
  }

  ~DynArray()
  {
    delete[] ptr;
  }

  DynArray& operator=(const DynArray& orig)
  {
    if (this != &orig) {
      DynArray(orig).swap(*this);
    }
    return *this;  
  }

  DynArray& operator=(DynArray&& orig)
  {
    DynArray(std::move(orig)).swap(*this);
    return *this;  
  }

  void swap(DynArray &other)
  {
    std::swap(other.ptr, ptr);
    std::swap(other.Capacity, Capacity);
    std::swap(other.Size, Size);
  }

  void push_back(const T& n)
  {
    if (Size >= Capacity) {
      grow();
    }
    ptr[Size] = n;
    ++Size;
  } 

  void pop_back()
  {
    if (Size <= 0) {
      throw std::runtime_error("Array is empty");  
    }
    ptr[Size - 1] = T();
    --Size;
  }

  T& front()
  {
    if (Size <= 0) {
      throw std::runtime_error("Array is empty");  
    }
    return ptr[0];
  }

  T& back()
  {
    if (Size <= 0) {
     throw std::runtime_error("Array is empty");  
    }
    return ptr[Size - 1];
  }

  T& at(int i)
  {
    if ((i < 0) || (i >= Size)) {
      throw std::out_of_range("Index out of range");
    }
    return ptr[i];
  }

  T& operator[](int i)
  {
    return ptr[i];
  }

  int size() const {
    return Size;
  }

  int capacity() const {
    return Capacity;
  }

private:
  T* ptr = nullptr;
  int Capacity = 0;
  int Size = 0;

  void grow()
  {
    std::cout << "grow" << std::endl;
    DynArray newArr(Capacity * 2);
    std::copy(ptr, ptr + Size, newArr.ptr);
    newArr.Size = Size;
    newArr.swap(*this);
  }
};

Затем вы должны рассмотреть возможность выбросить все это и просто использовать вместо него std::vector, который обрабатывает все эти детали для вас:

#include <vector>

template<class T>
class DynArray
{
public:

  DynArray(int n = 2)
  {
    vec.reserve(n);
  }

  void push_back(const T& n)
  {
    vec.push_back(n);
  } 

  void pop_back()
  {
    vec.pop_back();
  }

  T& front()
  {
    return vec.front();
  }

  T& back()
  {
    return vec.back();
  }

  T& at(int i)
  {
    return vec.at(i);
  }

  T& operator[](int i)
  {
    return vec[i];
  }

  int size() const {
    return vec.size();
  }

  int capacity() const {
    return vec.capacity();
  }

private:
  std::vector<T> vec;
};
0 голосов
/ 26 апреля 2019

Ваш конструктор копирования,

DynArray<T>(const DynArray& orig) {
 cout << "Copy" << endl;
 ptr = new T[Size];
 *ptr = *(orig.ptr);
}

не выполняет глубокое копирование.

Указатель, такой как ваш ptr, является просто указателем на ресурс (некоторый блок памяти).Если вы копируете его, блок памяти остается прежним и уникальным, вы просто скопировали его адрес.

Чтобы выполнить глубокое копирование, вам нужно выделить новый блок памяти (что вы делаете), а затем скопируйте его.Копировать блок памяти означает копировать каждый из его элементов.Здесь вы копируете только первое.Следовательно,

DynArray<T>(const DynArray& orig) {
 cout << "Copy" << endl;
 ptr = new T[Size];
 for (size_t i = 0; i < Size, i++)
     ptr[i] = orig.ptr[i];
}

Обратите внимание, что a[i] - это то же самое, что и *(a+i), что означает «вещь на i-й позиции в блоке памяти».

Еще лучше,Вы можете использовать std::copy, чтобы скрыть детали операции глубокого копирования, заменив цикл for:

std::copy(std::begin(orig.ptr), std::end(orig.ptr), std::begin(ptr));

в C ++ 11 или:

std::copy(orig.ptr, orig.ptr + Size, ptr);
...