предотвращение освобождения данных, когда вектор выходит за рамки

Есть ли способ передать право собственности на данные, содержащиеся в std::vector (на которые указывает, скажем, T * data), в другую конструкцию, предотвращая превращение «данных» висячего указателя после того, как вектор выходит из области видимости?

РЕДАКТИРОВАТЬ: Я НЕ ХОЧУ КОПИРОВАТЬ ДАННЫЕ (что было бы простым, но неэффективным решением).

В частности, я хотел бы иметь что-то вроде:

template<typename T>
    T* transfer_ownership(vector<T>&v){
    T*data=&v[0];
    v.clear();
    ...//<--I'd like to make v's capacity 0 without freeing data 
}

int main(){
    T*data=NULL;
    {
        vector<double>v;
        ...//grow v dynamically
        data=transfer_ownership<double>(v);
    }
    ...//do something useful with data (user responsible  for freeing it later)
   // for example mxSetData(mxArray*A,double*data) from matlab's C interface
}

Единственное, что приходит мне в голову, чтобы подражать этому:

{
    vector<double>*v=new vector<double>();
    //grow *v...
    data=(*v)[0];
}

а затем данные позже будут либо освобождены, либо (в моем случае) использованы как mxSetData(mxArrayA,doubledata). Однако это приводит к небольшой утечке памяти (структура данных для обработки емкости, размера и т. д. v... но, конечно, не сами данные).

Можно ли без течи?


person spirov    schedule 13.11.2009    source источник
comment
Это довольно запутанно. Кроме того, попробуйте спросить на форуме С++.   -  person Nzbuu    schedule 13.11.2009
comment
mxSetData и mxArray являются частью интерфейса C Matlab (через MEX-файлы C).   -  person spirov    schedule 13.11.2009
comment
Да, но на самом деле это не вопрос о Matlab. Люди, знающие Matlab, приходят сюда, чтобы помочь вам, и ничего не смогут сделать, если они не знают C++.   -  person quark    schedule 13.11.2009
comment
@spirov, я предлагаю немного прояснить ваши вопросы, потому что, хотя ответы отвечают на ваш вопрос, они кажутся неясными, почему вы спрашиваете. Насколько я могу судить, вы хотите знать две отдельные вещи (1) как сохранить память в вашем векторе в реальном времени после выхода из функции (т.е. как сохранить ее вне стека) и (2) как передать содержимое вектор во внешний API без копирования. Я думаю, что первое ясно из вашего вопроса, а второе - нет.   -  person quark    schedule 13.11.2009
comment
Да, я хотел бы (1) хотя и не обязательно хранить его в стеке (внешний API принимает динамически созданную память) и (2) (но эта часть проста, я просто даю &v[0]).   -  person spirov    schedule 14.11.2009


Ответы (5)


Простым обходным решением будет замена вектора на тот, который у вас есть:

vector<double> myown;

vector<double> someoneelses = foo();

std::swap( myown, someoneelses );

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

person xtofl    schedule 13.11.2009
comment
Простое, чистое и очень эффективное с точки зрения производительности решение. myown может быть создан в куче или где-то еще, чтобы жить после того, как кто-то еще выходит за рамки. - person Konstantin Tenzin; 13.11.2009
comment
или еще короче: foo().swap(myown); - person sellibitze; 13.11.2009
comment
Спасибо за указание на пользовательские распределители. Я унаследовал allocator‹T› от allocator_derived‹T› с настраиваемым поведением (стараясь не удалить, если какой-либо флаг установлен пользователем, то есть непосредственно перед выпуском вектора). Это прекрасно работает и не протекает, если соблюдать осторожность... на некоторых системах, но, к сожалению, не на всех. Например, он работает на Mac (gcc 4.2.1), но на 64-битном Linux (gcc 4.3) вектор, объявленный как vector‹T,allocator_derived‹T›, похоже, даже не использует производное поведение; Я не уверен, почему. - person spirov; 14.11.2009

Смысл использования std::vector заключается в том, чтобы не беспокоиться о данных в нем:

  • Сохраняйте свой вектор на протяжении всего приложения;
  • Передайте его по const-ref другим функциям (чтобы избежать ненужных копий);
  • И функции подачи ожидают указатель на T с &v[0].

Если вы действительно не хотите сохранять свой вектор, вам придется скопировать свои данные — вы не можете передать право собственности, потому что std::vector гарантирует, что он уничтожит свое содержимое, когда выйдет за пределы области действия. В этом случае используйте алгоритм std::copy().

person Julien-L    schedule 13.11.2009
comment
+1 за упоминание конструкции &v[0] для совместимости с C-подобным интерфейсом. - person Matthieu M.; 13.11.2009
comment
Третий элемент является ключевым, потому что передача содержимого вектора внешним функциям без копирования - большая часть того, что хочет OP. - person quark; 13.11.2009

Если ваш вектор содержит значения, вы можете только скопировать их (что происходит, когда вы вызываете std::copy, std::swap и т. д.). Если вы храните непримитивные объекты в векторе и не хотите их копировать (и использовать в другой структуре данных), рассмотрите возможность хранения указателей.

person Dmitry    schedule 13.11.2009

Что-то подобное работает для вас?

int main()
{
    double *data = 0;
    {
        vector<double> foo;
        // insert some elements to foo

        data = new double[foo.size()];
        std::copy(foo.begin(), foo.end(), &data[0]);
    }

    // Pass data to Matlab function.
    delete [] data;
    return 0;
}
person Michael Kristofik    schedule 13.11.2009

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

void f()
{
    std::vector<boost::shared_ptr<double> > doubles;
    InitVector(doubles);

    std::vector<boost::shared_ptr<double> > newDoubles(doubles);
}

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

person Steve Guidi    schedule 13.11.2009