Копировать данные из указателя или цепочки указателей (указатель объекта, шаблоны) - PullRequest
0 голосов
/ 24 июня 2011

Как реализовано push_back из stl::vector для создания копии любого типа данных .. может быть указатель, двойной указатель и т. Д. *

I 'm реализующий шаблонный класс, имеющий функцию push_back, почти аналогичную векторной.В этом методе копия аргумента должна быть вставлена ​​во внутреннюю выделенную память.

В случае, если аргумент является указателем или цепочкой указателей (указатель объекта);копия должна быть сделана из фактических данных, указанных. [обновлено согласно комментарию]

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

База кода выглядит следующим образом:

template<typename T>
class Vector
{
    public:
        void push_back(const T& val_in)
        {
            T a (val_in); // It copies pointer, NOT data.
            m_pData[SIZE++] = a; 
        }
}

Caller:

// Initialize my custom Vector class.
Vector<MyClass*> v(3);

MyClass* a = new MyClass();
a->a = 0;
a->b = .5;

// push MyClass object pointer
// now push_back method should create a copy of data 
// pointed by 'a' and insert it to internal allocated memory.
// 'a' can be a chain of pointers also.

// how to achieve this functionality?
v.push_back(a);

delete a;

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

Спасибо.

Ответы [ 5 ]

1 голос
/ 24 июня 2011

если у вас есть полиморфный объект (указанный объект может быть более специализированным, чем переменная), я предлагаю вам создать виртуальный метод clone (), который выделяет новый указатель с копией вашего объекта:

Base* A::clone() {
    A* toReturn = new A();
    //copy stuff
    return toReturn;
}

Если вы не можете изменить свой базовый класс, вы можете использовать RTTI, но я не буду подходить к этому решению в этом ответе.(Если вам нужно больше подробностей в этом решении, задайте вопрос о полиморфном клонировании с RTTI.)

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

void YourVector::push_back(Base* obj) {
    Base* copy = new Base(obj);
}

Но пахнет тем, что вам действительно нужен shared_ptr, доступен в <tr1/memory> (или <memory>, если вы используете C ++ 0x).

Обновление на основе комментариев

У вас также может быть список параметров двух шаблонов:

template <typename T>
struct CopyConstructorCloner {
    T* operator()(const T& t) {
        return new T(t);
    }
}

template <typename T, typename CLONER=CopyConstructorCloner<T> >
class MyList {
    CLONER cloneObj;
public:
    // ...
    void push_back(const T& t) {
        T* newElement = cloneObj(t);
        // save newElemenet somewhere, dont forget to delete it later
    }
}

При таком подходе можно определить новую политику клонирования для таких вещей, как указатели.

Тем не менее, я рекомендую вамиспользовать shared_ptrs.

0 голосов
/ 24 июня 2011

Нет необходимости вызывать new для данного типа данных T. Реализация push_back должна (должна) вызывать конструктор копирования или оператор присваивания. Память должна была быть выделена для хранения тех элементов, которые выдвигаются. Начальное выделение памяти не должно вызывать CTOR типа T. Что-то вроде:

   T* pArray; 
   pArray = (T*) new  BYTE[sizeof(T) * INITIAL_SIZE);

А затем просто поместите новый объект в pArray, вызвав оператор присваивания.

0 голосов
/ 24 июня 2011

Примерно так:

template<typename T>
class MyVector
{
    T*     data;          // Pointer to internal memory
    size_t count;         // Number of items of T stored in data
    size_t allocated;     // Total space that is available in data
                          // (available space is => allocated - count)

    void push_back(std::auto_ptr<T> item) // Use auto pointer to indicate transfer of ownership
    /*void push_back(T* item) The dangerous version of the interface */
    {
        if ((allocated - count) == 0)
        {    reallocateSomeMemory();
        }

        T*   dest = &data[count];  // location to store item

        new (dest) T(*item);       // Use placement new and copy constructor.
        ++count;
    }

    // All the other stuff you will need.
};

Редактировать на основе комментариев:

Чтобы позвонить, нужно сделать следующее:

MyVector<Plop>    data;

std::auto_ptr<Plop>   item(new Plop());   // ALWAYS put dynamically allocated objects
                                          // into a smart pointer. Not doing this is bad
                                          // practice.
data.push_back(item);

Я использую auto_ptr, потому что RAW-указатели плохие (т.е. в реальном коде C ++ (в отличие от C) вы редко видите указатели, они скрыты внутри интеллектуальных указателей).

0 голосов
/ 24 июня 2011

Я думаю, что для такого рода проблем лучше использовать умные указатели ex: boost :: shared_ptr или любую другую эквивалентную реализацию.

0 голосов
/ 24 июня 2011

Одним из решений является создание копии конструкции:

MyClass *p = new MyClass();
MyVector<MyClass*> v;
v.push_back(new MyClass(*p));

Обновление : Из обновленного вопроса вы можете определенно переопределить push_back

template<typename T>
class MyVector {
public:
  void push_back (T obj); // general push_back
  template<typename TYPE>  // T can already be a pointer, so declare TYPE again
  void push_back (TYPE *pFrom)
  {
    TYPE *pNew = new TYPE(*pFrom);
    // use pNew in your logic...
  }
};
...