Замена shared_ptr на unique_ptr

205
10 октября 2017, 04:56

Нужно заменить shared_ptr на unique_ptr, ибо первый не умеет работать с массивами. А unique_ptr не дает копировать себя. Что в этом случае делать?

**MatrixShape.hpp**  
#ifndef MATRIXSHAPE_HPP_INCLUDED
#define MATRIXSHAPE_HPP_INCLUDED
#include <memory>
#include "Shape.hpp"
class MatrixShape
{
public:
  MatrixShape(const std::shared_ptr<Shape> &);
  MatrixShape(const MatrixShape &);
  MatrixShape & operator=(const MatrixShape &);
  // HERE std::unique_ptr <std::shared_ptr<Shape>[]>::pointer operator[](const size_t &index) const; // HERE
  void addShape(const std::shared_ptr<Shape> &);
  void print() const;
private:
  std::unique_ptr <std::shared_ptr<Shape>[]> matrix_;
  size_t rows_, columns_;
  bool checkOverlap(const std::shared_ptr<Shape> &, const std::shared_ptr<Shape> &) const;
};
#endif
#include <iostream>
#include <iomanip>
#include <stdexcept>
#include <cmath>
#include "MatrixShape.hpp"
MatrixShape::MatrixShape(const std::shared_ptr<Shape> &shape):
  matrix_(new std::shared_ptr<Shape>[1]),
  rows_(1),
  columns_(1)
{
  if (shape == nullptr)
  {
    throw std::invalid_argument("invalid pointer");
  }
  matrix_[0] = shape;
}
MatrixShape::MatrixShape(const MatrixShape &matrix_shape):
  rows_(matrix_shape.rows_),
  columns_(matrix_shape.columns_)
{
  std::unique_ptr <std::shared_ptr<Shape>[]> matrix_copy(new std::shared_ptr<Shape>[rows_ * columns_]);
  for (size_t i = 0; i < (rows_ * columns_); i ++)
  {
    matrix_copy[i] = matrix_shape.matrix_[i];
  }
  matrix_.swap(matrix_copy);
}
MatrixShape & MatrixShape::operator=(const MatrixShape &matrix_shape)
{
  if (this != &matrix_shape)
  {
    rows_ = matrix_shape.rows_;
    columns_ = matrix_shape.columns_;
    std::unique_ptr <std::shared_ptr<Shape>[]> matrix_copy(new std::shared_ptr<Shape>[rows_ * columns_]);
    for (size_t i = 0; i < (rows_ * columns_); i ++)
    {
      matrix_copy[i] = matrix_shape.matrix_[i];
    }
    matrix_.swap(matrix_copy);
  }
  return *this;
}
std::unique_ptr <std::shared_ptr<Shape>[]>::pointer MatrixShape::operator[](const size_t &index) const
{
  if (index > rows_)
  {
    throw std::invalid_argument("index out of range");
  }
  return matrix_.get() + index * columns_;
}
void MatrixShape::addShape(const std::shared_ptr<Shape> &shape)
{
  if (shape == nullptr)
  {
    throw std::invalid_argument("invalid pointer");
  }
  size_t i = rows_ * columns_;
  size_t desired_row = 1; 
  while (i > 0)
  {
    i --;
    if (checkOverlap(matrix_[i], shape))
    {
      desired_row = i / columns_ + 2;
    }
  }
  size_t rows_temp = rows_;
  size_t columns_temp = columns_;
  size_t free_columns = 0;
  if (desired_row > rows_)
  {
    rows_temp ++;
    free_columns = columns_;
  }
  else
  {
    size_t j = (desired_row - 1) * columns_;
    while (j < (desired_row * columns_))
    {
      if (matrix_[j] == nullptr)
      {
        free_columns ++;
      }
      j ++;
    }
    if (free_columns == 0)
    {
      columns_temp ++;
      free_columns = 1;
    }
  }
  std::unique_ptr <std::shared_ptr<Shape>[]> matrix_temp(new std::shared_ptr<Shape>[rows_temp * columns_temp]);
  for (size_t i = 0; i < rows_temp; i ++)
  {
    for (size_t j = 0; j < columns_temp; j ++)
    {
      if (i >= rows_ || j >= columns_)
      {
        matrix_temp[i * columns_temp + j] = nullptr;
        continue;
      }
      matrix_temp[i * columns_temp + j] = matrix_[i * columns_ + j];
    }
  }
  matrix_temp[desired_row * columns_temp - free_columns] = shape;
  matrix_.swap(matrix_temp);
  rows_ = rows_temp;
  columns_ = columns_temp;
}
bool MatrixShape::checkOverlap(const std::shared_ptr<Shape> &shape_1, const std::shared_ptr<Shape> &shape_2) const
{
  if (shape_1 == nullptr || shape_2 == nullptr)
  {
    return false;
  }
  rectangle_t shape_1_frame_rect = shape_1 -> getFrameRect();
  rectangle_t shape_2_frame_rect = shape_2 -> getFrameRect();
  return ((fabs(shape_1_frame_rect.pos.x - shape_2_frame_rect.pos.x)
    < ((shape_1_frame_rect.width / 2) + (shape_2_frame_rect.width / 2)))
    && ((fabs(shape_1_frame_rect.pos.y - shape_2_frame_rect.pos.y)
    < ((shape_1_frame_rect.height / 2) + (shape_2_frame_rect.height / 2)))));
}
void MatrixShape::print() const
{
  for (size_t i = 0; i < rows_; i ++)
  {
    for (size_t j = 0; j < columns_; j ++)
    {
      if (matrix_[i * columns_ + j] != nullptr)
      {
        std::cout << std::setw(16) << std::left << matrix_[i * columns_ + j] -> getName();
      }
    }
    std::cout << "\n";
  }
}
Answer 1

Не надо ничего заменять.

Хоть std::shared_ptr и не поддерживает массивы в явном виде, мы можем обмануть его, передав указатель на начало массива так, будто это указатель на единственный элемент.

Ведь как работают умные указатели в C++? Они принимают указатель, над которым нужно установить владение, и deleter — функциональный объект, знающий, как корректно освободить данные по этому указателю.

Так как указатель на массив тождественен указателю на первый элемент массива (то есть эти два понятия взаимозаменяемые), то единственное, что нам остаётся — передать умному указателю deleter, работающий с массивами. Кстати, если вы выделяете массив оператором new[], то нам подойдёт стандартный std::default_delete<T[]>.

Для удобства использования обернём создание массива в шаблонный метод:

#include <memory>
template<typename T>
std::shared_ptr<T> allocateArray(size_t n)
{
    return std::shared_ptr<T>(new T[n], std::default_delete<T[]>());
}

У данного способа есть один недостаток — вам придётся где-то хранить размер массива.

READ ALSO
Обработка исключений с++

Обработка исключений с++

Как обработать исключение, которое возникает при попытке инициализировать значение за пределами массива, или при чтении из-за его пределовПробовал...

252
shared_ptr для динамического массива

shared_ptr для динамического массива

Имеется небольшой класс для создания массива обернутого в shared_ptr, при запуске возникает ошибка : "Невозможно преобразовать int* в int", не могу...

319
Изменение текстового поля на форме из потока QML QT C++

Изменение текстового поля на форме из потока QML QT C++

Пытаюсь изменить значение текстового поля из другого потока, но почему-то не работает( Имеется класс MyThread:

277
проект на mpi Test.exe вызвал срабатывание точки останова

проект на mpi Test.exe вызвал срабатывание точки останова

Простой код с использованием MPIПри сборке в релиз отладка останавливается в указанном месте на ветке else

331