Нельзя хранить указатель на временную переменную, размещенную в стеке.Когда временный элемент перестает существовать в конце своей области видимости, указатель больше не действителен.
Например, со следующим кодом (обратите внимание, для краткости я пропустил инициализацию значений массива в этих примерах):
struct Foo
{
int* data;
Foo( int* d )
: data( d )
{
}
};
Foo makeFoo( size_t size )
{
int bar[ size ]; // note this in invalid c++, only allowed by a gcc extension
return Foo( bar );
}
int main()
{
Foo f = makeFoo( 4 );
std::cout << f.data[ 0 ]; // undefined behaviour, f.data points to and array that no longer exists
}
Массив bar
не существует после окончания makeFoo
, а возвращенный указатель Foo
data
указывает на память, которая была освобождена.Обратите внимание, что если вы запустите приведенный выше код, он, скорее всего, выведет правильный вывод, так как хотя массив больше не существует, его значения не будут перезаписаны, так как никакая другая функция не будет использовать стек до того, как мы распечатаем вывод.
Наиболее C ++ решение этой проблемы - использовать std::vector
:
struct Foo
{
std::vector< int > data;
Foo( const std::vector< int >& data )
: data( d ) // make a copy of the temporary vector, no more undefined behaviour
{
}
};
Foo makeFoo( size_t size )
{
std::vector< int > bar( size );
return Foo( bar );
}
Если ваше задание не позволяет вам использовать std::vector
(я ненавижу задания, которые пытаются научить вас C ++, не позволяя вамчтобы использовать ключевые функции языка и стандартной библиотеки), вам нужно будет использовать указатели:
struct Foo
{
int* data;
Foo( int* d )
: data( d )
{
}
};
Foo makeFoo( size_t size )
{
int* bar = new int[ size ];
return Foo( bar );
}
Вам нужно будет убедиться, что вы внедрили деструктор, который вызывает delete[]
в массиве, иначе у вас будетутечка памяти и обратите внимание на правило из трех и реализуйте или удалите конструктор копирования и оператор присваивания.
Чтобы избежать этой проблемы, мы должны использовать std::unique_ptr
, который удаляет массивдля нас и избегает правила 3/5 выпуска:
struct Foo
{
std::unique_ptr< int[] > data;
Foo(std::unique_ptr< int[] >&& d)
: data( std::move( d ) )
{
}
};
Foo makeFoo(size_t size)
{
std::unique_ptr< int[] > bar( new int[ size ] );
return Foo( std::move( bar ) );
}
Обратите внимание, что невозможно скопировать std::unique_ptr
, мы должны использовать std::move
для перемещения значенийа не копировать их.Вам также потребуется использовать std::move:
для перемещения объектов Foo, а не для их копирования.См. https://en.cppreference.com/w/cpp/language/move_constructor для получения информации о том, как движущиеся объекты работают в C ++ (может быть немного продвинутым для новичка, но это ключевая особенность современного C ++).Если вы не можете разобраться с движущимися объектами, вы можете вместо этого использовать std::shared_ptr
(просто замените std::unique_ptr
на std::shared_ptr
в приведенном выше примере и удалите std::move
s), который можно копировать, но учтите, что если вы копируетеFoo
объект, обе копии будут указывать на одинаковые данные.