2016-09-26 5 views
0

Недавно я узнал об использовании unique_ptr, надеясь, что я смогу избавиться от накладных расходов на копирование, присущих хранилищам STL, хранящимся по значению. Однако я столкнулся с действительно странным поведением и не мог понять, почему.Неожиданная перезапись «unique_ptr» в векторе

struct LargeObj 
{ 
    int id; 
    LargeObj(int _id) : id(_id) 
    { 
     cout << "[" << this << "] is constructed\n"; 
    } 
    ~LargeObj() 
    { 
     cout << "[" << this << "] is destroyed\n"; 
    } 
    // Simulate huge data size 
    int data[10000]; 
}; 

int main(int argc, char **argv) 
{ 
    vector<unique_ptr<LargeObj>> store_by_pointer; 
    for (int i = 0; i < 10; i++) 
    { 
     LargeObj obj(i); 
     store_by_pointer.push_back(unique_ptr<LargeObj>(&obj)); 
    } 
    for (auto ite = store_by_pointer.begin(); ite != store_by_pointer.end(); ite++) 
    { 
     printf("ID: %d\n", (*ite)->id); 
    } 
    return 0; 
} 

Выход следующего

[00000000001A6180] is constructed 
[00000000001A6180] is destroyed 
[00000000001A6180] is constructed 
[00000000001A6180] is destroyed 
[00000000001A6180] is constructed 
[00000000001A6180] is destroyed 
[00000000001A6180] is constructed 
[00000000001A6180] is destroyed 
[00000000001A6180] is constructed 
[00000000001A6180] is destroyed 
[00000000001A6180] is constructed 
[00000000001A6180] is destroyed 
[00000000001A6180] is constructed 
[00000000001A6180] is destroyed 
[00000000001A6180] is constructed 
[00000000001A6180] is destroyed 
[00000000001A6180] is constructed 
[00000000001A6180] is destroyed 
[00000000001A6180] is constructed 
[00000000001A6180] is destroyed 
ID: 9 
ID: 9 
ID: 9 
ID: 9 
ID: 9 
ID: 9 
ID: 9 
ID: 9 
ID: 9 
ID: 9 

Мой вопрос почему каждый push_back заменить все элементы перед ним и, таким образом, сделать все объекты так же, как последнее толкание, который в этом случае является LargeObj с идентификатором 9.

ответ

0

Вы должны построить unique_ptr из указателя, возвращаемого new, а не адрес переменной стеки - или даже лучше, использовать make_unique. Вы также должны последовательно использовать std::cout или printf последовательно. Попробуйте снова:

#include <vector> 
#include <memory> 
#include <iostream> 

struct LargeObj 
{ 
    int id; 
    LargeObj(int _id) : id(_id) 
    { 
     std::cout << "[" << this << "] is constructed\n"; 
    } 
    ~LargeObj() 
    { 
     std::cout << "[" << this << "] is destroyed\n"; 
    } 
    // Simulate huge data size 
    int data[10000]; 
}; 

int main(int argc, char **argv) 
{ 
    std::vector<std::unique_ptr<LargeObj>> store_by_pointer; 
    for (int i = 0; i < 10; i++) 
    { 
     store_by_pointer.push_back(std::make_unique<LargeObj>(i)); 
    } 
    for (const auto& ptr : store_by_pointer) 
    { 
     std::cout << "ID: " << ptr->id << "\n"; 
    } 
    return 0; 
} 

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

На предсказывая, что неопределенное поведение будет делать: Это полезная вещь, чтобы сделать при отладке (так что вы можете понять поведение вашего кода), но это крайне опасно писать код, чтобы полагаться на него. Это изменится - в самых причудливых и бесполезных способностях.

отметить также, что я включил заголовки, так что это становится Minimal, Complete, and Verifiable Example (и используется std:: и использовать для итерации цикла.

+0

Я действительно ценю ваш подробный ответ и исправление моей грамматики. –