Как реализовать оператор копирования для такой структуры C ++? - PullRequest
0 голосов
/ 31 января 2011

Итак, имея

struct ResultStructure
{
    ResultStructure(const ResultStructure& other)
    {
        // copy code in here ? using memcpy ? how???  
    }
    ResultStructure& operator=(const ResultStructure& other)
    {
        if (this != &other) {
            // copy code in here ?
        }
        return *this
    }
    int length;
    char* ptr;
};

Как реализовать «конструктор копирования» и «оператор присваивания»? (извините - я C ++ nube)

Обновление: sbi и другие спрашивают - почему я хочу вручную работать с необработанной памятью? Мой ответ прост - в студенческом проекте, частью которого я являюсь, сейчас мы используем много библиотек C, таких как, например, OpenCV OpenAL и FFmpeg, и это еще не все. В настоящее время, используя C ++, мы пытаемся создать прямое шоу на основе графиков, например, кроссплатформенную библиотеку, которая была бы полезна для прямой трансляции и обработки видео. Наши элементы графика в настоящее время используют пары char * и int для обмена данными. Для приведения данных к подписанным элементам мы сейчас используем raw memcpy. Я хочу пойти дальше и сделать так, чтобы наши графические элементы основывались на шаблоне C ++. Так, чтобы один элемент графа мог обмениваться текущими данными элемента графа с другими элементами графа, а эти данные, которыми он делится, были бы структурой, которая содержала бы не один символ * одно целое, а любое количество полей данных и почти любые элементы внутри. Вот почему мне нужно понять, как создать базовую структуру C ++, которая реализует «конструктор копирования» и «оператор присваивания», чтобы я мог использовать новые для нас алгоритмы приведения данных, такие как

void CastData(T item){
    for(size_t i = 0 ; i < FuncVec.size(); i++){
        T dataCopy = item;
        FuncVec[i](dataCopy);
    }
}

вместо используемых в настоящее время

void CastData(char * data, int length){
    for(size_t i = 0 ; i < FuncVec.size(); i++){
        char* dataCopy = new char[length];
        memcpy(dataCopy, data, length);
        FuncVec[i](dataCopy, length);
                    delete[] dataCopy;
    }
}

Ответы [ 7 ]

9 голосов
/ 31 января 2011

Если вы используете std::string вместо char*, вам даже не нужно будет писать operator= или конструктор копирования.Сгенерированный компилятором код очень хорошо справился бы с вашей задачей.

Но в качестве общего решения (для некоторых других сценариев) используйте идиому копирования и замены:

Исключительный C ++ Херб Саттер описал это очень подробно.Я бы порекомендовал вам прочитать предметы из этой книги.В настоящее время вы можете прочитать эту статью онлайн:

7 голосов
/ 31 января 2011

Возможно, вы захотите объяснить , почему вы хотите вручную обработать необработанную память.Я давно этого не делал, это то, для чего std::string и std::vector предназначены:

struct ResultStructure
{
    // nothing else needed

    std::string data; // or std::vector<char>
};

Но если вам действительно нужно сделать это нелегко (это домашняя работа?)затем имейте в виду, что на первый взгляд на удивление трудно понять это правильно.Например, наивная реализация оператора присваивания может выглядеть следующим образом:

// DON'T TRY THIS AT HOME!!
ResultStructure& ResultStructure::operator=(const ResultStructure& rhs)
{
  delete[] ptr;                               // free old ressource
  ptr = new char[rhs.length];                 // allocate new resourse
  std::copy(rhs.ptr, rhs.ptr+rhs.length, ptr; // copy data
  length = rhs.length;
}

Если кто-то случайно назначил объект себе (что может произойти, если у вас есть только две ссылки, и вы их не подозреваетессылаться на тот же объект), то это приведет к фатальной неудаче.
Кроме того, что если new выдает исключение?(Может выдать std::bad_alloc, если память исчерпана.) Тогда мы уже удалили старые данные и не выделили новые данные.Однако указатель все еще указывает на то место, где раньше находились старые данные (на самом деле, я думаю, что это определяется реализацией, но мне еще предстоит увидеть реализацию, которая изменяет ptr при удалении), и деструктор класса (you знал , что классу нужен деструктор, верно?) Затем попытается удалить часть данных по адресу, где данные не выделены.Это неопределенное поведение .Лучшее, на что вы можете надеяться, это то, что он немедленно падает.

Самый простой способ сделать это - использовать идиому Copy-And-Swap :

struct ResultStructure
{
    ResultStructure(const ResultStructure& other)
     : ptr(new char[rhs.length]), length(rhs.length)
    {
        std::copy(rhs.ptr, rhs.ptr+rhs.length, ptr);
    }

    ~ResultStructure() // your class needs this
    {
      delete[] ptr;
    }

    ResultStructure& operator=(ResultStructure rhs) // note: passed by copy
    {
        this->swap(rhs);
        return *this
    }

    void swap(const ResultStruct& rhs)
    {
      using std::swap;
      swap(length, rhs.length); 
      swap(ptr, rhs.ptr); 
    }

    std::size_t length;
    char* ptr;
};

Обратите внимание, что я добавил деструктор, изменил оператор присваиваниячтобы передать аргумент для каждой копии (нам нужен конструктор копирования, вызываемый для выделения памяти), и добавили функцию-член swap.В соответствии с соглашением функция swap() никогда не генерирует и является быстрой, предпочтительно O (1).

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

4 голосов
/ 31 января 2011

Простое решение - использовать std::string вместо char* member.

Тогда сгенерированный компилятором конструктор копирования и оператор присваивания копии просто работают.

Как правило, и особенно как новички, не имеют необработанных указателей.

Приветствия и hth.,

2 голосов
/ 31 января 2011

Как уже было сказано, и, как было рекомендовано в вопросе, из которого он возник, вам, вероятно, следует повторно использовать существующий контейнер. По крайней мере, код будет правильным:)

Для образовательных целей, давайте рассмотрим эту структуру:

class Result
{
public:

private:
  size_t length; // can't really be negative, right ?
  char* data;
};

Здесь нам нужно явное управление памятью. Это подразумевает, в частности, следование правилу Три (скажем, спасибо Фреду)

Давайте начнем с создания нашего объекта:

Result::Result(): length(0), data(0) {}

Result::Result(size_t l, char* d): length(0), data(0)
{
  if (!d) { return; }

  data = new char[l]; // this may throw, so we do it first

  length = l;
  memcpy(data, d, l);
}

Теперь мы можем реализовать традиционные операторы:

// Swap
void Result::swap(Result& other)
{
  using std::swap;

  swap(length, other.length);
  swap(data, other.data);
}

// Copy Constructor
Result::Result(Result const& other): length(0), data(0)
{
  if (!other.length) { return; }

  data = new char[other.length];
  length = other.length;
  mempcy(data, other.data, length);
}

// Assignemt Operator
Result& Result::operator=(Result other)
{
  this->swap(other);
  return *this;
}

// !IMPORTANT!
// Destructor
Result::~Result()
{
  delete[] data; // use the array form of delete
                 // no need to test for nullity, it's handled
}
1 голос
/ 31 января 2011

это работа std::vector<char> - или это домашняя работа?

вектор заменит и length, и распределение за ptr.вектор - это идиома c ++, и вы не подружитесь с другими разработчиками c ++, если будете реализовывать свои классы, как вы описали.Конечно, есть угловые случаи, но стандартные контейнеры, такие как vector, являются значениями по умолчанию.

vector знает, как копировать символы так же, как и себя, и реализации оптимизируются и тестируются.вот как явно реализовать копию ctor / assign с использованием вектора:

struct ResultStructure {
    ResultStructure(const ResultStructure& other) : d_chars(other.d_chars) {
    }

    ResultStructure& operator=(const ResultStructure& other) {
        if (this != &other) {
            this->d_chars = other.d_chars;
        }
        return *this;
    }
    std::vector<char> d_chars;
};
0 голосов
/ 31 января 2011

Я думаю, что это должно сработать:

struct ResultStructure
{
    ResultStructure(const ResultStructure& other);
    ResultStructure& operator=(const ResultStructure& other);

    int length;
    char* ptr;
};

ResultStructure::ResultStructure(const ResultStructure& other):length(other.length)
{
    ptr = (char*)malloc(length);
    memcpy(ptr, other.ptr, length);
}

ResultStructure& ResultStructure::operator=(const ResultStructure& other)
{
    length = other.length;
    ptr = (char*)malloc(length);
    memcpy(ptr, other.ptr, length);
    return *this;
}

Пожалуйста, помните об освобождении ptr в деструктореЧто хранится в ptr?Если текст, почему бы не использовать std :: string?В любом случае вы можете использовать std :: vector.Конструкторам будет намного легче, чем ...

0 голосов
/ 31 января 2011

Каким образом память, которой выделено ptr точек?

, если используется новая, выделите с помощью new, установите length, а затем скопируйте

other.length = length;
other.ptr = new char[length];
memcpy( other.ptr, ptr, length );

Если вы 'перераспределение памяти с помощью malloc, вместо вызова new вместо вызова *1011*.Если вы используете какую-то другую схему памяти, выясните это.:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...