C++ 意外的运行时错误(分段错误)

C++ 意外的运行时错误(分段错误),c++,new-operator,binaryfiles,dynamic-memory-allocation,delete-operator,C++,New Operator,Binaryfiles,Dynamic Memory Allocation,Delete Operator,我目前正在优化一个内存密集型应用程序,以占用更少的内存。在下面的代码中,我试图动态地分配文件流对象ifstream和ofstream,以便在不再需要文件流使用后完全释放它们。代码对于流的的分配/取消分配非常有效,但在运行时由于ifstream的内存内容取消分配时可能出现分段错误而崩溃。以下是原始代码的一个片段: #include <fstream> using namespace std; // Dummy class to emulate the issue at hand cl

我目前正在优化一个内存密集型应用程序,以占用更少的内存。在下面的代码中,我试图动态地分配文件流对象
ifstream
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";

}
#包括
使用名称空间std;
//虚拟类来模拟手头的问题
类虚拟{
私人:
int-randINT;
需要静态布尔;
公众:
dummy(){randINT=rand();}
int getVal(){return randINT;}
};
bool dummy::isseed=false;
int main(int argc,const char*argv[]{
//二进制文件I/O从这里开始
虚拟对象*obj;
流*输出文件;
ifstream*输入文件;
outputFile=newofstream(“bFile.bin”,ios::binary);
如果(!(*outputFile).fail()){
obj=新假人;
cout最多读取
count
个字符,并将它们放置在
地址
。它不会像您似乎相信的那样在
地址
创建任何对象。
地址
必须指向至少
count*sizeof(char\u type)的有效内存块
大小。通过传递
delete
d对象的地址,您违反了该条件:您的代码已损坏,并且分段错误不是意外的

编辑

你所做的是,简短的UB。当你这样做时,没有任何保证,任何关于按哪个顺序发生的逻辑都是无效的。程序可以做任何事情,包括立即崩溃、运行一段时间然后崩溃,或者

在您的例子中,我怀疑
std::basic_istream::read()
写入未受保护的内存会导致某些数据被覆盖,例如另一个对象的地址,这会导致分段错误。但这纯粹是推测,不值得研究

在您的情况下,不会创建任何对象。二进制文件只包含一些字节。
read()
将它们复制到提供的地址,该地址不是为此目的保留的。要避免UB,只需添加

obj = new dummy;
在执行
之前,请阅读
以创建对象

如果您想重新使用上一个对象的内存,可以使用(请参见该链接的第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
char*buffer=nullptr;//指向潜在内存缓冲区的指针
如果(书面){
如果(!缓冲区)
buffer=新字符[sizeof(dummy)];//保留内存缓冲区
auto pobj=new(buffer)dummy(args);//在buffer中创建对象
写入(缓冲区,sizeof(虚拟));//写入对象的字节
pobj->~pobj();//销毁对象,但不要释放缓冲区
}
如果(阅读){
如果(!缓冲区)
buffer=new char[sizeof(dummy)];//如果之前写入,则不需要
read(buffer,sizeof(dummy));//将字节读入对象
auto pobj=reinterpret_case(buffer);//这里没有保证
使用(pobj);//对读取的对象执行一些操作
pobj->~pobj();//析构函数对象
}
删除[]缓冲区;//释放保留内存
请注意,如果读取未生成有效对象,则该对象的后续使用,即对其析构函数的调用,可能会崩溃


然而,所有这些微观优化无论如何都是毫无意义的(只有当您能够避免对
新建
删除
调用时,才值得这么做)不要浪费你的时间。< /P>请你提供问题。A你来自java或C++背景吗?因为在C++中,你不必使用<代码>新< /Cord>创建对象实例。这会导致指针变少,指针也少了,因为错误使用指针是崩溃最常见的原因。tFile=流的新文件(“bFile.bin”,ios::binary)
是一个非常糟糕的主意…你是故意想打败吗?对我来说最突出的是使用
obj
你删除它指向的内存,但保留那个地址并试图重用它…这就是我要从这里开始的地方。@Baum mit Augen我在p我仍然不明白。如果问题是“传递已删除对象的地址”,那么存储的值将不会显示,因为读取()时会出错已调用。当您从二进制文件读取对象时,该对象不是已经创建了吗?@hecate欢迎使用未定义的行为!!。您可以使用作用域来控制对象何时解除分配,请查找RAII。