我目前正在优化内存密集型应用程序以占用更少的内存。我在以下代码中尝试做的是动态分配文件流对象ifstream
和ofstream
,以便在不再需要它之后准确地释放它们。该代码完美地用于ofstream
的分配/解除分配,但在运行时崩溃,因为当ifstream
的内存内容被解除分配时可能存在分段错误。以下是原始代码的片段:
#include <fstream>
using namespace std;
// Dummy class to emulate the issue at hand
class dummy {
private:
int randINT;
static bool isSeeded;
public:
dummy() { randINT=rand(); }
int getVal() { return randINT; }
};
bool dummy::isSeeded=false;
int main(int argc, const char* argv[]) {
// Binary file I/O starts here
dummy * obj;
ofstream * outputFile;
ifstream * inputFile;
outputFile=new ofstream("bFile.bin",ios::binary);
if (!(*outputFile).fail()) {
obj=new dummy;
cout << "Value to be stored: " << (*obj).getVal() << "\n";
(*outputFile).write((char *) obj, sizeof(*obj)); // Save object to file
(*outputFile).close();
delete obj;
// don't assign NULL to obj; obj MUST retain the address of the previous object it pointed to
} else {
cout << "Error in opening bFile.bin for writing data.\n";
exit(1);
}
delete outputFile; // This line throws no errors!
inputFile=new ifstream("bFile.bin",ios::binary);
if (!(*inputFile).fail()) {
(*inputFile).read((char *) obj,sizeof(dummy)); // Read the object of type 'dummy' from the binary file and allocate the object at the address pointed by 'obj' i.e. the address of the previously de-allocated object of type 'dummy'
cout << "Stored Value: " << (*obj).getVal() << "\n";
(*inputFile).close();
} else {
cout << "Error in opening bFile.bin for reading data.\n";
exit(1);
}
delete inputFile; // Runtime error is thrown here for no reason!
cout << "\n-----END OF PROGRAM-----\n";
}
当用户唤起保存功能时,上面的代码将对象保存到二进制文件。如上面的代码所述,inputFile
指针的解除分配会引发运行时错误。
请注意我使用clang(更具体地说,是clang ++)来编译项目。
提前致谢。
答案 0 :(得分:2)
std::basic_istream::read(char_type*address, streamsize count)
最多可读取count
个字符,并将其放在address
。它似乎相信,不在address
创建任何对象。 address
必须指向一个有效的内存块,其大小至少为count*sizeof(char_type)
。通过传递delete
d对象的地址,您违反了该条件:您的代码已损坏且分段错误并非意外。
修改强>
你正在做的是undefined behaviour,简短的UB。当你这样做时,没有任何保证,任何关于什么发生的顺序无效的逻辑。该程序可以执行任何操作,包括立即崩溃,运行一段时间然后崩溃,或"make demons fly out of your nose"。
在您的情况下,我怀疑std::basic_istream::read()
写入未受保护的内存会导致某些数据被覆盖,例如另一个对象的地址,这会导致分段错误。但这是纯粹的推测,并不值得追求。
在您的情况下,不会创建任何对象。二进制文件只包含一些字节。 read()
将它们复制到提供的地址,但不是为此目的而保留的。要避免使用UB,只需添加
obj = new dummy;
在read
之前创建一个对象。
如果要重新使用上一个对象的内存,可以使用placement new(请参阅该链接的第9和第10点)。例如
char*buffer = nullptr; // pointer to potential memory buffer
if(writing) {
if(!buffer)
buffer = new char[sizeof(dummy)]; // reserve memory buffer
auto pobj = new(buffer) dummy(args); // create object in buffer
write(buffer,sizeof(dummy)); // write bytes of object
pobj->~pobj(); // destruct object, but don't free buffer
}
if(reading) {
if(!buffer)
buffer = new char[sizeof(dummy)]; // not required if writing earlier
read(buffer,sizeof(dummy)); // read bytes into object
auto pobj = reinterpret_case<dummy*>(buffer); // no guarantees here
use(pobj); // do something with the object read
pobj->~pobj(); // destruct object
}
delete[] buffer; // free reserved memory
请注意,如果读取不生成有效对象,则该对象的后续使用(即对其析构函数的调用)可能会崩溃。
然而,所有这些微观优化无论如何都是毫无意义的(如果你可以避免很多对new
和delete
的调用,那么这是值得做的。不要浪费你的时间。