2011-01-28 4 views
2

Надуманный пример ниже показывает дубликаты адресов внутри цикла. Такое же явление проявляется, когда я строю как с оптимизацией, так и без нее на MinGW GCC 4.5.0. Проблема, которую я вижу, заключается в том, что когда я создаю вектор внутри цикла, внутренний массив иногда, по-видимому, занимает те же адреса памяти, что и вектор, созданный на предыдущей итерации. Мой вопрос в том, что вызывает это?Повторное использование внутреннего адресного пространства с векторами, создаваемыми внутри цикла

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

#include <iostream> 
using std::cout; 
using std::endl; 
#include <vector> 
using std::vector; 
#include <cstdlib> 
//using NULL; 
#include <algorithm> 
using std::for_each; 
#include <tr1/functional> 
using std::tr1::bind; 
using std::tr1::placeholders::_1; 

template<typename T> 
void write_address(T const & arg){ 
    cout<<" "<<&arg; 
} 

template <typename T> 
void write_container_addresses(vector<T> const & container){ 
    for_each(container.begin(),container.end(), 
      write_address<T>); 
    cout<<endl; 
} 

template <typename T> 
void write_container_container_addresses(
     vector<vector<T> > const & container){ 
    for_each(container.begin(),container.end(), 
      write_container_addresses<T>); 
} 

int main() { 
    vector<vector<int> > stacked_vector(5); 
    for_each(stacked_vector.begin(),stacked_vector.end(), 
      bind(&vector<int>::reserve,_1,9)); 
    for_each(stacked_vector.begin(),stacked_vector.end(), 
      bind(&vector<int>::resize,_1,5,0)); 

    cout<<"stacked vector addresses"<<endl; 
    for (size_t i = 0;i<stacked_vector.size();i++){ 
     write_container_addresses<int>(stacked_vector[i]); 
    } 
    cout<<endl; 

    cout<<"another print of stacked vector addresses"<<endl; 
    write_container_container_addresses(stacked_vector); 
    cout<<endl; 

    vector<vector<int> > other_stacked_vector; 

    cout<<"other vector addresses"<<endl; 
    for (size_t i = 0;i<stacked_vector.size();i++){ 
     other_stacked_vector.push_back(vector<int>()); 
     other_stacked_vector.back().reserve(9); 
     other_stacked_vector.back().resize(5,0); 
     write_container_addresses<int>(other_stacked_vector[i]); 
    } 
    cout<<endl; 

    cout<<"another write of other vector addresses"<<endl; 
    write_container_container_addresses<int>(other_stacked_vector); 

    return 0; 
} 

Выход:

C:\workspace\test_of_pointer_vector_addresses\Debug>test_of_pointer_vector_addre 
sses.exe 
stacked vector addresses 
0x3e3e08 0x3e3e0c 0x3e3e10 0x3e3e14 0x3e3e18 
0x3e3e38 0x3e3e3c 0x3e3e40 0x3e3e44 0x3e3e48 
0x3e3e68 0x3e3e6c 0x3e3e70 0x3e3e74 0x3e3e78 
0x3e3e98 0x3e3e9c 0x3e3ea0 0x3e3ea4 0x3e3ea8 
0x3e3ec8 0x3e3ecc 0x3e3ed0 0x3e3ed4 0x3e3ed8 

another print of stacked vector addresses 
0x3e3e08 0x3e3e0c 0x3e3e10 0x3e3e14 0x3e3e18 
0x3e3e38 0x3e3e3c 0x3e3e40 0x3e3e44 0x3e3e48 
0x3e3e68 0x3e3e6c 0x3e3e70 0x3e3e74 0x3e3e78 
0x3e3e98 0x3e3e9c 0x3e3ea0 0x3e3ea4 0x3e3ea8 
0x3e3ec8 0x3e3ecc 0x3e3ed0 0x3e3ed4 0x3e3ed8 

other vector addresses 
0x3e3f10 0x3e3f14 0x3e3f18 0x3e3f1c 0x3e3f20 
0x3e3f10 0x3e3f14 0x3e3f18 0x3e3f1c 0x3e3f20 
0x3e3f10 0x3e3f14 0x3e3f18 0x3e3f1c 0x3e3f20 
0x3e2430 0x3e2434 0x3e2438 0x3e243c 0x3e2440 
0x3e2430 0x3e2434 0x3e2438 0x3e243c 0x3e2440 

another write of other vector addresses 
0x3e3f40 0x3e3f44 0x3e3f48 0x3e3f4c 0x3e3f50 
0x3e3f60 0x3e3f64 0x3e3f68 0x3e3f6c 0x3e3f70 
0x3e24c8 0x3e24cc 0x3e24d0 0x3e24d4 0x3e24d8 
0x3e24e8 0x3e24ec 0x3e24f0 0x3e24f4 0x3e24f8 
0x3e2430 0x3e2434 0x3e2438 0x3e243c 0x3e2440 

ответ

3

Проблема, которую я вижу в этом цикле:

cout<<"other vector addresses"<<endl; 
for (size_t i = 0;i<stacked_vector.size();i++){ 
    other_stacked_vector.push_back(vector<int>()); 
    other_stacked_vector.back().reserve(9); 
    other_stacked_vector.back().resize(5,0); 
    write_container_addresses<int>(other_stacked_vector[i]); 
} 
cout<<endl; 

вы пишете адрес после каждого суб-вектор был добавлен в сложенной векторе. Проблема в том, что при следующей итерации и вызове push_back() адреса, которые были отображены для предыдущей итерации, могут быть более недействительными. Это вызвано тем фактом, что операции push_back, возможно, придется перераспределять память, заставляя все векторы в векторном стеке перемещаться.

Более простая демонстрация заключается в следующей:

vector<int> vec; 
vec.push_back(1); 
int* p = &vec[0]; 
vec.push_back(2); 
cout << *p << endl; //ERROR: p possibly no longer valid. 

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

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

+0

Чтобы расширить это, OP необходимо будет использовать вектор указателей на векторы (или в идеале что-то вроде boost :: ptr_vector), чтобы гарантировать, что внутренние векторы не перемещаются. – bdonlan

+0

Это звучит правильно для меня. Минимальной модификацией для исправления проблемы было бы добавить строку 'other_stacked_vector.reserve (stacked_vector.size());' сразу после объявления 'other_stacked_vector'. –