根据如何创建我的 unique_ptr,它要么有效,要么导致执行时出现
malloc(): corrupted top size
错误。
这会导致上述错误
#include <tuple>
#include <string>
#include <memory>
#include <fstream>
#include <iostream>
#include <filesystem>
std::tuple<std::unique_ptr<uint8_t*>, size_t> getBinaryData( const std::filesystem::path &sourcePath )
{
std::ifstream filestream ( sourcePath, std::ios::binary );
const size_t filesize = std::filesystem::file_size(sourcePath);
std::cout << "Input file size: " << filesize << " bytes\n";
std::unique_ptr<uint8_t*> buffer = std::make_unique<uint8_t*>( new uint8_t[ filesize ] );
if ( !filestream.read( ( char* )( buffer.get() ), filesize ) )
{
std::cout << << "Reading from file " << sourcePath << " failed." << "\n";
}
filestream.close();
return { std::move( buffer ), filesize };
}
int main()
{
auto [memoryPtr, length] = getBinary( "myfile.bin" );
}
这有效:
std::tuple<std::unique_ptr<uint8_t, std::default_delete<uint8_t[]>>, size_t> getBinaryData( const std::filesystem::path &sourcePath )
{
std::ifstream filestream ( sourcePath, std::ios::binary );
const size_t filesize = std::filesystem::file_size(sourcePath);
std::cout << "Input file size: " << filesize << " bytes\n";
std::unique_ptr<uint8_t, std::default_delete<uint8_t[]>> buffer( new UInt8_t[ filesize ] );
if ( !filestream.read( ( char* )( buffer.get() ), filesize ) )
{
std::cout << "Reading from file " << sourcePath << " failed." << "\n";
}
filestream.close();
return { std::move( buffer ), filesize };
}
但我不喜欢第二种解决方案,因为我必须到处写
std::default_delete<uint8_t[]>
。
你知道我在第一种方法中做错了什么吗?
了解为什么这不起作用也很有趣:
std::tuple<std::unique_ptr<uint8_t*>, size_t> getBinaryData( const std::filesystem::path &sourcePath )
{
std::ifstream filestream ( sourcePath, std::ios::binary );
const size_t filesize = std::filesystem::file_size(sourcePath);
std::cout << "Input file size: " << filesize << " bytes\n";
std::unique_ptr<uint8_t*> buffer( new uint8_t[ filesize ] );
if ( !filestream.read( ( char* )( buffer.get() ), filesize ) )
{
std::cout << "Reading from file " << sourcePath << " failed." << "\n";
}
filestream.close();
return { std::move( buffer ), filesize };
}
new
/delete
和 new[]
/delete[]
进行了严格区分。您不能使用一个运算符分配内存,然后使用另一个运算符释放内存。std::unique_ptr<Type>
在销毁时调用 delete
,并且 std::make_unique<Type>()
调用 new
。 std::unique_ptr<Type[]>
在销毁时调用 delete[]
,并且 std::make_unique<Type[]>
接受数组的大小作为其参数并调用 new[]
。std::unique_ptr<Type*>
在销毁时调用delete
并释放Type*
类型的指针,而不是它指向的内存。
std::unique_ptr<uint8_t*> buffer = std::make_unique<uint8_t*>( new uint8_t[ filesize ] );
在这里,您分配了一个由 filesize
的
uint8_t
元素组成的数组,然后分配 1 个
uint8_t*
类型的对象,并将数组的地址复制到该变量中。该地址的地址存储在
buffer
中。
if ( !filestream.read( ( char* )( buffer.get() ), filesize ) )
buffer.get()
返回
uint8_t**
类型的值,一个指向指针大小的内存块的指针。您以 C 风格将其转换为
char*
并尝试向其中写入
filesize
字节。如果
filesize
大于
sizeof(uint8_t*)
,则您正在分配的内存之外进行写入。此时任何事情都可能发生,如果你的程序只是崩溃了,那你就很幸运了。此时您很可能会损坏内部堆结构。
return { std::move( buffer ), filesize };
万一 filesize <= sizeof(uint8_t*)
你(正确地)返回
buffer
变量。最终它将被销毁,并且
buffer
(指针的大小)拥有的内存将被正确释放。如果堆在上面被损坏,这将是库函数可能“注意到”它的点。不管怎样,您分配的
filesize
大小的块将永远不会被释放,内存已经“泄漏”。在另一次尝试中,你写了
std::unique_ptr<uint8_t*> buffer( new uint8_t[ filesize ] );
在这里,您再次分配 filesize
的
uint8_t
元素,并将该块的地址写入
buffer
。不会发生内存泄漏。不幸的是,在销毁时
buffer
将调用
delete
(因为它无法判断您是否存储了数组或只是单个值),并且您将得到
new[]
/
delete
不匹配,这将再次导致最好的情况是 立即使你的程序崩溃,并可能在你的应用程序中引入看似神秘的错误。 你可以做什么:
//no manual "new"!
std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(filesize);
//the rest is the same sans changing uint8_t* to uint8_t[]
但是,大多数时候您最好使用 std::vector<uint8_t>
来代替。数组指针不存储大小(这就是您必须返回元组的原因),并且可以轻松寻址分配内存之外的值。