2017-01-02 25 views
-1

В следующем случае моего объекта выходит из области видимости и получить доступ к недопустимому указателю:Как предотвратить временный выход из сферы действия?

struct Animal 
{ 
    char* buffer; 
    Animal() { buffer = new char[100]; } 
    ~Animal() { delete[]buffer; } 
}; 


int main() 
{ 
    vector<Animal> list; 

    { 
     Animal dog; 
     list.push_back(dog); 
    } 

    list[0].buffer[50] = 7; // Buffer's been deleted, can't access it 
} 

я думаю, что лучший способ, чтобы предотвратить это было бы построить объект Animal на месте в векторе, но дон Не знаю, как это сделать. Я думал о выполнении:

list.push_back(Dog()); 

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

Edit: Благодаря Реми Лебо я узнал, вы можете построить вектор элемент непосредственно в векторе, нет временных, ни копирования, с помощью функции:

template< class... Args > 
void emplace_back(Args&&... args); 

Я не знаю, как VARIADIC шаблон аргументов работает, но описание:

Добавляет новый элемент в конец контейнера. Элемент равен , построенный по std :: allocator_traits :: construct, который обычно использует новое размещение для создания элемента на месте в месте , предоставляемом контейнером. Аргументы арг ... направляются в конструктор как станд :: вперед (арг) ....

+0

Я не понимаю вопроса. Вы просите вас помещать вещи, которые не являются конструктивными по умолчанию в 'std :: vector'? –

+0

Это не то, что делает 'reserve()'. –

+1

«После этого я использую свой vectorOfAnimals [0] и получаю недопустимый указатель« Что вы имеете в виду под «недопустимым указателем»? Где это? – songyuanyao

ответ

3

Проблема заключается не в том, что временная выходит из области видимости. Реальная проблема заключается в том, что Animal нарушает Rule of three, не реализуя конструктор копирования или оператор присваивания копии.

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

Добавить конструктор копирования выделить новую память:

struct Animal 
{ 
    char* buffer; 

    Animal() { 
     buffer = new char[100]; 
    } 

    Animal(const Animal &src) { 
     buffer = new char[100]; 
     std::copy(src.buffer, src.buffer+100, buffer); 
    } 

    ~Animal() { 
     delete[] buffer; 
    } 

    Animal& operator=(const Animal &rhs) { 
     if (this != &rhs) { 
      std::copy(rhs.buffer, rhs.buffer+100, buffer); 
     } 
     return *this; 
    } 
}; 

В качестве альтернативы, используйте std::vector вместо исходного указателя, и пусть компилятор генерировать подходящий конструктор копирования, копирующий оператор присваивания и деструктор для вас:

struct Animal 
{ 
    std::vector<char> buffer; 
    Animal() : buffer(100) {} 
}; 

Или просто выделить память статически, а не динамически:

struct Animal 
{ 
    char buffer[100]; 
}; 
+0

Спасибо. Это моя вина за то, что я не понимаю. Я хочу узнать, как избежать копирования. Я думал, что есть способы построить объект в векторе. – Zebrafish

+0

@TitoneMaurice ничего в вашем вопросе даже не намекает на вашу цель избежать копирования. До C++ 11 вы не можете избежать копирования. В C++ 11 и более поздних версиях вы можете использовать 'emplace_back()' вместо 'push_back()'. Но вы все равно должны следовать Правилу Трех (и Правилу Пяти в C++ 11 и более поздних) всякий раз, когда класс управляет ресурсами, которые не могут быть мелко-скопированы. –

+0

vector :: emplace_back() не работает. Когда я делаю myVector.emplace_back (myStruct (arg1, arg2)); он сначала конструирует myStruct, а затем копирует конструкцию нового в векторе. Grrrr, одно за раз. Если emplace_back() не работает, я думаю, я должен задать вопрос об этом. – Zebrafish