Копировать данные из указателя или цепочки указателей (указатель объекта, шаблоны)

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

Я реализую класс шаблона, имеющий функцию 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 для выполнения задач, но в целях эксперимента я пишу класс шаблона, который делает то же самое.

Спасибо.


person Azodious    schedule 24.06.2011    source источник
comment
Во-первых, ваш вопрос очень расплывчатый и двусмысленный. Ваш аргумент является указателем, но вы хотите вставить копию аргумента. Похоже, вы просто хотите скопировать указатель, а не объект, на который он указывает. Надеюсь, ваш первый фрагмент кода даже не скомпилируется, и поэтому трудно догадаться, для чего он на самом деле предназначался. Наконец, ваш код вообще ужасен. Не используйте new везде. От этого в моей голове звенят все тревожные звоночки. Вы (ab) используете C++, как если бы это была Java или C# (или как если бы вы прочитали несколько онлайн-руководств)   -  person jalf    schedule 24.06.2011
comment
Да все верно. возможно, я злоупотребляю С++ и не очень разбираюсь в копировании, указателе, ссылке и т. д., поэтому я спросил об этом здесь, чтобы исправить эти вещи.   -  person Azodious    schedule 24.06.2011
comment
Вам следует немного поработать над вопросом. Сделайте требования четкими, в настоящее время неясно, должен ли аргумент push_back быть указателем или указатели являются внутренней деталью (т.е. внутренняя выделенная память: это единое выделение для всех содержащихся объектов, или отдельное выделение для каждого объекта). Собственно говоря, вы должны начать объяснять, какие у вас есть требования, которые не удовлетворяются стандартными библиотеками, чтобы было понятно, что вам нужно.   -  person David Rodríguez - dribeas    schedule 24.06.2011
comment
@David: я хочу создать класс шаблона с функцией push_back, похожей на вектор STL. Я обновлю вопрос с более подробной информацией.   -  person Azodious    schedule 24.06.2011


Ответы (5)


если у вас есть полиморфный объект (указанный объект может быть более специализированным, чем переменная), я предлагаю вам создать виртуальный метод с именем 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.

person André Puel    schedule 24.06.2011
comment
Вызывающий может быть кем угодно, и его нельзя заставить предоставить метод клонирования. и если я использую шаблоны, последняя строка в коде будет выглядеть как T copy = new T*(obj) ... и компилятор ее не принимает. - person Azodious; 24.06.2011
comment
В STL, когда у вас есть, например, вектор‹MyClass›, вектор копирует данный объект в push_back, и если это указатель, указатель копируется, и вызывающая сторона должна гарантировать, что указатель останется действительным. В вашем случае может быть хорошей идеей иметь разные реализации для векторов на основе указателей и для векторов на основе ссылок. - person André Puel; 24.06.2011

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

person Sasha    schedule 24.06.2011

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

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

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

person Ajay    schedule 24.06.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...
  }
};
person iammilind    schedule 24.06.2011
comment
Переданный аргумент может быть использован вызывающим абонентом после отправки в вектор. Я не должен это менять. - person Azodious; 24.06.2011
comment
Пожалуйста, смотрите обновленный вопрос. поэтому нельзя использовать простую конструкцию копирования. - person Azodious; 24.06.2011
comment
но это все еще не общий, я думаю. MyVector<MyClass**> v; и push_back вызывается с &p - person Azodious; 24.06.2011
comment
@iammilind: Почему вы получаете аргумент в качестве ссылки? Это не имеет смысла, аргумент push_back должен быть либо const& (общий случай), либо копией (частный случай, если вы хотите перегрузить указатели), но неконстантная ссылка не имеет смысла. - person David Rodríguez - dribeas; 24.06.2011
comment
@ Дэвид, я отредактировал свой ответ. Идея состоит в том, чтобы иметь отдельный указатель push_back для их копирования. Это просто демонстрация идеи. Тем не менее, это не полный код. - person iammilind; 24.06.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) вы редко видите указатели, они скрыты внутри умных указателей).

person Martin York    schedule 24.06.2011
comment
Ошибка в вызывающем модуле: ошибка C2664: «Vector‹T›::push_back»: невозможно преобразовать параметр 1 из «MyClass *» в «std::auto_ptr‹_Ty›» - person Azodious; 24.06.2011
comment
если я изменю подпись на void push_back(const T& item) ... она не будет скопирована. - person Azodious; 24.06.2011
comment
@Azodious: Посмотрите на правку. Вам нужно прочитать об умных указателях. - person Martin York; 24.06.2011
comment
но таким образом вызывающая сторона вынуждена использовать Smart Pointer. можно ли этого избежать? и да. я должен читать больше. - person Azodious; 24.06.2011
comment
@Martin: я думаю, что это меняет семантику не так, как предполагалось. В частности, он крадет право собственности на исходный объект, вызывая передачу права собственности. - person David Rodríguez - dribeas; 24.06.2011
comment
@David Rodríguez: в вопросе прямо говорится, что он собирается его удалить. - person Martin York; 24.06.2011
comment
@Azodious: Да. Замените std::auto_ptr<T> на T*. Но весь смысл в том, чтобы заставить пользователей поступать правильно. В противном случае их код будет отстойным. - person Martin York; 24.06.2011