C ++ Как глубоко копировать структуру с неизвестным типом данных? - PullRequest
1 голос
/ 07 июня 2010

У меня есть «провайдер данных», который сохраняет свои выходные данные в структуре определенного типа, например

struct DATA_TYPE1{
   std::string data_string;
};

тогда эта структура должна быть преобразована в общий тип данных, я думал о void * или char *, потому что "промежуточный" объект, который копирует и хранит его в своем двоичном дереве, должен уметь хранить много разных типов таких структурных данных.

struct BINARY_TREE_ENTRY{
   void * DATA;
   struct BINARY_TREE_ENTRY * next;
};

эта пустота * затем берется другим объектом который сбрасывает пустоту * обратно в (struct DATA_TYPE1 *) чтобы получить исходные данные. поэтому отправитель и получатель знать о типе данных DATA_TYPE1, но не о копировании промежуточный объект.

но как промежуточный объект может глубоко копировать содержимое различные структуры, когда он не знает тип данных, только void * и у него нет способа скопировать реальное содержимое; dynamic_cast не работает для void *;

«промежуточный» объект должен делать что-то вроде:

void store_data(void * CASTED_DATA_STRUCT){
   void * DATA_COPY = create_a_deepcopy_of(CASTED_DATA_STRUCT);
   push_into_bintree(DATA_COPY);
}

простое решение было бы, что отправляющий объект не удалить отправленную структуру данных, пока получающий объект не получил ее, но отправляющие объекты динамически создаются и удаляются, до того, как получатель получил данные от промежуточного объекта, для асинхронной связи, поэтому я хочу скопировать это.

вместо преобразования в void * я также пытался конвертировать на указатель суперкласса которого промежуточное копирование объект знает о том, что наследуется всеми типы данных структур:

struct DATA_BASE_OBJECT{
public:
    DATA_BASE_OBJECT(){}
DATA_BASE_OBJECT(DATA_BASE_OBJECT * old_ptr){
         std::cout << "this should be automatically overridden!" << std::endl;
    }
    virtual ~DATA_BASE_OBJECT(){}
};

struct DATA_TYPE1 : public DATA_BASE_OBJECT {
public:
    string str;
    DATA_TYPE1(){}
    ~DATA_TYPE1(){}
    DATA_TYPE1(DATA_TYPE1 * old_ptr){
         str = old_ptr->str;
    }
};

и соответствующая запись в двоичном дереве будет:

struct BINARY_TREE_ENTRY{
       struct DATA_BASE_OBJECT * DATA;
       struct BINARY_TREE_ENTRY * next;
    };

и затем скопировать неизвестный тип данных, я пытался в классе который просто получает неизвестный тип данных в виде struct DATA_BASE_OBJECT * (до того как была пустота *):

void * copy_data(DATA_BASE_OBJECT * data_that_i_get_in_the_sub_struct){
     struct DATA_BASE_OBJECT * copy_sub = new DATA_BASE_OBJECT(data_that_i_get_in_the_sub_struct);
     push_into_bintree(copy_sub);
}

Затем я добавил конструктор копирования в DATA_BASE_OBJECT, но если структура DATA_TYPE1 сначала приводится к DATA_BASE_OBJECT, а затем скопированный, включенный подобъект DATA_TYPE1 также не копируется.

Затем я подумал, что узнать размер реального объекта. скопировать, а затем просто скопировать его, но байты не хранятся в одна строка и как я могу узнать реальный размер в памяти struct DATA_TYPE1, которая содержит std :: string?

Какие другие методы c ++ доступны для глубокой копии неизвестного типа данных (и, возможно, получить информацию о типе данных как-то еще во время во время выполнения)?

Ответы [ 5 ]

9 голосов
/ 07 июня 2010

Если у вас есть void *, вы не сможете извлечь из него информацию о типах. Вот почему void * очень и очень редко используются в программах на C ++ (я, честно говоря, не могу вспомнить, когда в последний раз я их использовал) - ваш подход здесь полностью ошибочен. Если вам нужны универсальные контейнеры, тип которых известен во время компиляции, используйте шаблоны. Если вам нужны контейнеры, тип которых изменяется во время выполнения, производные от базового класса и использование контейнеров указателей базового класса. И не пишите свои собственные контейнеры (за исключением, возможно, в качестве учебных упражнений) - C ++ имеет совершенно хорошее двоичное дерево, реализованное как std :: set и std :: map.

И, наконец, не используйте ALL CAPS для имен типов C ++.

0 голосов
/ 07 июня 2010

спасибо за быстрые ответы.

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

для меня теперь я могу сделать это с помощью шаблонов для каждого типа данных, который используется, но мне действительно интересно с точки зрения разработки кода, если есть не проще в C ++. ты прав, что я родом из с я буду медленно себя вести при программировании на с ++.

я попробовал этот подход с наследованием базового класса, который это уже ограничение для кодирования, потому что каждый новый тип данных должен быть закодирован для наследования базовой структуры, но это не работает, потому что только вызывает конструктор копирования базовой структуры, которая единственное, что знает «объект копирования» (потому что он ничего не знает о производных структурах datetypes), может делать, не работает.

конструктор копирования производной структуры не вызывается, или я делаю что-то здесь не так? я попробовал это с оператором присваивания, но он не делает копию, и как только исходные данные будут удалены, я думаю, это свисающий указатель.

На данный момент работает так

  1. «отправляющий объект» создает структуру DATA_TYPE1 * с новым DATA_TYPE1 ...
  2. и заполняет его данными
  3. и отменяет его *
  4. и сообщает «объекту копирования» об этом указателе
  5. затем «отправляющий объект» удаляется, содержимое пустоты * все еще живо
  6. "copy-object" хранит только пустоту *
  7. "получающий объект" получает эту пустоту * позднее
  8. и возвращает его в struct DATA_TYPE1 * и использует его данные
  9. и окончательно удаляет данные

, поэтому здесь не выполняется копирование, просто передается указатель на исходные данные от одного объекта к другому.

спасибо за совет по поводу сериализации, я думал об этом, но на данный момент я хочу потренироваться с чистым C ++, чтобы решить эту проблему.

0 голосов
/ 07 июня 2010

Какие другие методы c ++ доступны для глубокой копии неизвестного типа данных (и, возможно, для получения информации о типе данных каким-либо другим образом во время выполнения)

Здесь вы видите сериализацию: возможность помещать различные объекты в двоичный поток, перемещать поток - и, возможно, сохранять его, а затем получать копию исходного содержимого обратно из потока. *

Вы можете реализовать это самостоятельно или основывать свою реализацию на существующих библиотеках.

Одна из опций, которую я использовал, это boost :: serialization. В основном вам необходимо реализовать либо метод serialize во всей иерархии классов, либо метод save и load во всей иерархии классов.

Код довольно прост. См. здесь для учебника.

0 голосов
/ 07 июня 2010

но как промежуточный объект может глубоко копировать содержимое различных структур, когда он не знает тип данных, только void * и у него нет метода для копирования реального содержимого;dynamic_cast не работает для void *;

Вы просто не можете этого сделать.Блок void *, представляющий определенные данные, должен содержать несколько указателей (например, std :: string выделяет память динамически).И вы не будете знать, где именно они хранятся, поэтому не будет возможности глубоко копировать данные, не создавая беспорядка.

dynamic_cast не работает для void *;

Вы можете попытаться преобразовать void * в некоторый базовый тип, а затем dynamic_cast во что угодно.Однако я не могу гарантировать, что будет безопасно использовать это, например, с объектом, созданным с помощью множественного наследования.Будет безопаснее использовать некоторый абстрактный класс для обмена данными вместо указателей void *.

0 голосов
/ 07 июня 2010

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

Я предлагаю использовать шаблоны. Это позволит вам по-прежнему иметь дело с разными типами данных, используя один и тот же класс / функции, а также гарантирует безопасность типов намного лучше, чем указатели void.

Редактировать: Для дальнейшего выяснения, почему указатели void вообще существуют: указатели void были использовались для такого рода вещей в C. Однако, хотя вы все еще можете использовать их в C ++, обычно это не рекомендуется, потому что есть лучшие решения.

Кроме того, вы упоминаете глубокое копирование. Все типы, которые будут использоваться с базой данных, должны либо реализовывать функцию deepCopy (это подход в стиле ООП), либо вы можете просто забыть перегрузить оператор присваивания для всех типов, которые вы используете с DATA_BASE:)

...