LINUX.ORG.RU

C++ [part3]


0

0

Тот же преславутый класс A :) ... 
только используется подругому ... 
но радости это не уменьшает

vector<A> func()
{
    vector<A> rez;
    for ( unsigned int i = 0 ; i < 3 ; i ++ )
    {
        cerr << "i = " << i << endl;
        cerr << "A buf(i): "; 
        A buf(i); 
        cerr << endl;
        cerr << "rez.push: "; 
        rez.push_back(buf); 
        cerr << endl;
    }
    cerr << "\n\nFormirovanie zakoncheno\n\n";
    return rez;
}

int main()
{
    vector<A> answ = func();
    cerr << "teper clear ...\n";
    answ.clear();
    cerr << endl;
    return 0;
}

Резалт выполнения.
================================================
i = 0
A buf(i):  A(0)
rez.push: copy(0)
 ~A(0) i = 1
A buf(i):  A(1)
rez.push: copy(0) copy(1)  ~A(0)
 ~A(1) i = 2
A buf(i):  A(2)
rez.push: copy(0) copy(1) copy(2)  ~A(0)  ~A(1)
 ~A(2)

Formirovanie zakoncheno

teper clear ...
 ~A(0)  ~A(1)  ~A(2)
================================================
Все понятно, все в порядке. 
только вот при и = 1 copy(0) 
а при и = 2 и сору(0) copy(1) ... 
откуда взялось? Что такое там делает вектор? а если у меня
700 -1500 элементов ... помоему неэфективно как-то.
Объясните пожалуйста.
anonymous

Делай через "умные" указатели.

template<class T>
class auto_ref {
private:
   void dec_ref() {
     if(ref && !--ref->cnt){
         delete ref->p;
         delete ref;
     }
   }
   void copy(const auto_ref<T>& ar){
      dec_ref();
      ref = ar.ref;
      if(ref)ref->cnt++;
   }
public:
   struct Ref {
      unsigned cnt;
      T* p;
   }
   auto_ref(){ref=0;}
   auto_ref(const auto_ref<T>& ar) {copy(ar);}
   ~auto_ref(){dec_ref();}
   auto_ref<T>& operator=(const auto_ref<T>& ar)
      {copy(ar); return *this;}
   T* operator->(){return ref->p;}
   const T* operator->()const{return ref->p;}
   T& operator*(){return *ref->p;}
   const T& operator*()const{return *ref->p;}

   mutable Ref* ref;
};

vector<auto_ref<A> > func()
{
    vector<auto_ref<A> > rez;
    for ( unsigned int i = 0 ; i < 3 ; i ++ )
    {
        cerr << "i = " << i << endl;
        cerr << "A buf(i): "; 
        auto_ref<A> buf(new A(i)); 
        cerr << endl;
        cerr << "rez.push: "; 
        rez.push_back(buf); 
        cerr << endl;
    }
    cerr << "\n\nFormirovanie zakoncheno\n\n";
    return rez;
}
/*
либо так:
*/

auto_ref<vector<A> > func()
{
    auto_ref<vector<A> > rez(new vector<A>);
    for ( unsigned int i = 0 ; i < 3 ; i ++ )
    {
        cerr << "i = " << i << endl;
        cerr << "A buf(i): "; 
        A buf(i); 
        cerr << endl;
        cerr << "rez.push: "; 
        rez->push_back(buf); 
        cerr << endl;
    }
    cerr << "\n\nFormirovanie zakoncheno\n\n";
    return rez;
}
======================================================
Можно и скомбинировать: auto_ref<vector<auto_ref<A> > >

kosmonavt
()
Ответ на: комментарий от kosmonavt

Я не силен в шаблонах (только начл изучать), но мне кажется что здесь
auto_ref надо применять не к самому вектору, а к его сождержимому:
просто vector<auto_ref<A>>.


И вобще не проще ли в исходном коде сделать:
rez.reserve(MAX_VECTOR_SIZE);
чтобы не было лишних reallocation.

Vinick ★★
()
Ответ на: комментарий от Vinick

>Я не силен в шаблонах (только начл изучать), но мне кажется что здесь
>auto_ref надо применять не к самому вектору, а к его сождержимому:
>просто vector<auto_ref<A>>. 

Прошу прощения - просмотрел. Действительно нужен auto_ref<vector<auto_ref<A> > >.

Но может проще сделать так:
void func(vector<A> & rez)
{
    for ( unsigned int i = 0 ; i < 3 ; i ++ )
    {
        cerr << "i = " << i << endl;
        cerr << "A buf(i): "; 
        A buf(i); 
        cerr << endl;
        cerr << "rez.push: "; 
        rez.push_back(buf); 
        cerr << endl;
    }
    cerr << "\n\nFormirovanie zakoncheno\n\n";
}

int main()
{
    vector<A> answ;
    answ.reserve(3); // в общем случае вместо 3 поставить 
                     // максимальный размер масива.
    func(answ);
    cerr << "teper clear ...\n";
    answ.clear();
    cerr << endl;
    return 0;
}

Vinick ★★
()
Ответ на: комментарий от Vinick

Для vector<A> "лишние обращения" будут
Там, примерно, сделано так (объясняю без шаблонов):

class vector_A {
public:
.......
   void reserve(unsigned sz) {
      A* tmp_ptr = (A*)new char[sz*sizeof(A)];
      for(unsigned i=0; i<size_of_arr; i++)
         new(tmp_ptr+i) A(arr[i]);
      for(unsigned i=0; i<size_of_arr; i++)
         arr[i].~A();
      delete[](char*)arr;
      arr = tmp_ptr;
   }
   void push_buf(const A& a) {
      new(arr+size()) A(a);
      size_of_arr++;
   }
   vector_A(const vector_A& vec) {
      arr = (A*)new char[vec.size_of_arr*sizeof(A)];
      for(unsigned i=0; i<size_of_arr; i++)
         new(arr+i) A(arr[i]);
   }
   vector_A& operator=(const vector_A& vec) {
      delete[](char*)arr;
      arr = (A*)new char[vec.size_of_arr*sizeof(A)];
      for(unsigned i=0; i<size_of_arr; i++)
         new(arr+i) A(arr[i]);
   }
.......
   mutable A* arr;
};

kosmonavt
()

> откуда взялось?

Как было справедливо замечено, взялось в результате реаллокирования (никто не обещал, что область получится расширить в том же месте и избежать копирования) и предотвращается вызовом reserve.

Еще можно вместо вектора использовать deque. Честно говоря, я не помню, гарантируется ли отсутствие копирований стандартом, но в реальной жизни копирований в deque нет.

anonymous
()
Ответ на: комментарий от anonymous

>реальной жизни копирований в deque

Значит deque либо на указателях, либо пустышка.
(Вроде бы на связанных списках делают);

в vector я бы делал так:

template<T>
class vector {
......
   vector(){
       arr = (T*)malloc(sizeof(T)*_capacity);
       if(!arr) throw out_of_memory();
   }
   void push_back(const T& t) {
       if(_size==_capacity){
          T* tmp = (T*)realloc(arr,sizeof(T)*(_capacity+add));
          if(!tmp) throw out_of_memory();
          arr = tmp;
          _capacity+=add;
       }
       new(arr+_size) T(t);
       size++;
   }
........
  T* arr;
};
=============================
Возражения будут?



kosmonavt
()
Ответ на: комментарий от kosmonavt

>Значит deque либо на указателях, либо пустышка. >(Вроде бы на связанных списках делают);

На каких указателях? Какая пустышка? Насколько я знаю, deque имеет двухуровневую структуру и представляет собой список маленьких массивчиков.

>Возражения будут?

Я не очень понимаю, кто такие _size и size, и мне немного лень думать, правильно ли то, что инкремент делается после placemant new, а не перед ним, но самое, пожалуй, существенное допущение - realloc. Отнюдь не всякому A (т.е. T) он полезен для здоровья.

anonymous
()
Ответ на: комментарий от anonymous

>Я не очень понимаю, кто такие _size и size

Пардон, не пропечатал. size - это _size.

>Отнюдь не всякому A (т.е. T) он полезен для здоровья.

На сколько я помню, это предубеждение пошло от new[], который сохраняет размермер массива для delete[], т.е. чтобы тот корректно вызывались деструкторы для объектов. В данном случае этого нет. Или вы что-то другое имеете в виду?

kosmonavt
()
Ответ на: комментарий от kosmonavt

>На сколько я помню, это предубеждение пошло от new[], который сохраняет размермер массива для delete[], т.е. чтобы тот корректно вызывались деструкторы для объектов. В данном случае этого нет. Или вы что-то другое имеете в виду?

Не любой объект можно побайтно копировать.

Предубеждение, ага.

anonymous
()
Ответ на: комментарий от anonymous

понял, исправил, всем спасибо большое!!!

anonymous
()
Ответ на: комментарий от kosmonavt

> Хм. Тут не _копирование_. Тут _перемещение_.

Не любой объект можно побайтно перемещать.

anonymous
()
Ответ на: комментарий от anonymous

>Не любой объект можно побайтно перемещать.

Из-за деструкторов. В случае, когда объект в стеке, нельзя, потому что деструктор сработает на объекте, которого не должно быть, но состояние памати остается таким же как и до перемещения. То же самое, когда
вызывается delete. Здесь же "ручное" управление деструкторами.

Короче, число вызовов конструкторов равно числу вызовов деструкторов.
Ваши опасения напрасны.

kosmonavt
()
Ответ на: комментарий от kosmonavt

P.S.

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

kosmonavt
()
Ответ на: комментарий от kosmonavt

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

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

Неконструктивное возражение состоит в том, что ни одна из известных мне реализаций контейнерных классов не поступает предложенным Вами образом.

На самом деле, ответы на такие вопросы нужно искать в стандарте.

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

anonymous
()
Ответ на: комментарий от anonymous

>Соображения про операционные системы и перемещения не вполне имеют отношение к делу

С этим согласен, перегнул.

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

По указателям на свои члены отпадает - члены не переносятся.

>объект может хранить указатели сам на себя

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

Хотя трудно представить себе перенос или копирование такого зацикленного объекта в стиле C++.

kosmonavt
()
Ответ на: комментарий от kosmonavt

>Хотя трудно представить себе перенос или копирование такого зацикленного объекта в стиле C++

Именно для этого случая.

kosmonavt
()
Ответ на: комментарий от kosmonavt

>По указателям на свои члены отпадает - члены не переносятся.

Не понял в начале, что вы имели в виду. Да сам иногда так делал.

Плохо, когда не используются возможносте функций реалокации.
Я вот о чем подумал - для этого нужно добавить в интерфейс объекта
или контейнера функцию коррекции указателей.

struct move_A {
   void operator(A& a){
      // корректируем указатели
      ...
   }
};

template<class T, class pointer_correct>
class vector {
...
    void push_back(const A& a){
        ........
        tmp = realloc(arr,...
        ........
        pointer_correct pc;
        for(unsigned i=0; i<_size; i++)
           pc(arr[i]);
    }
...
   T* arr;
};

kosmonavt
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.