-1

현재 메모리 사용량이 적은 응용 프로그램을 최적화하여 메모리를 덜 차지합니다. 다음 코드에서 수행하려는 작업은 더 이상 필요없는 파일 스트림 객체를 정확하게 해제하기 위해 파일 스트림 객체 ifstreamofstream을 동적으로 할당하는 것입니다. 이 코드는 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 ++)을 사용하여 프로젝트를 컴파일하고 있습니다.

미리 감사드립니다.

+1

질문을 [편집]하여 [mcve]를 제공해주십시오. –

+3

Java 또는 C# 배경에서 왔습니까? 왜냐하면 C++에서는 객체의 인스턴스를 만들기 위해'new'를 사용할 필요가 없기 때문입니다. 그리고 포인터가 적어지고, 포인터를 잘못 사용하는 것이 충돌의 가장 일반적인 이유이기 때문에 포인터가 적을수록 좋습니다. –

+2

'outputFile = new ofstream ("bFile.bin", ios :: binary);은 매우 나쁜 생각입니다. 의도적으로 [RAII] (https://en.wikipedia.org/wiki/)를이기려고합니까? Resource_acquisition_is_initialization)? – WhiZTiM

답변

2

std::basic_istream::read(char_type*address, streamsize count)count자를 읽고이를 address에 넣습니다. 아니요은 믿을 것 같아서 address에 개체를 만듭니다. address은 유효한 크기의 메모리가 적어도 count*sizeof(char_type) 크기 여야합니다. delete 개체의 주소를 전달하면 해당 조건을 위반하게됩니다. 코드가 손상되어 세그먼트 화 오류가 예기치 않은이 아닙니다. 당신이 무슨 일을하는지

편집

undefined behaviour, 짧은 UB입니다. 그렇게하면 어떤 것도 보장되지 않으며 어떤 순서로 어떤 일이 일어날 지에 대한 논리가 무효가됩니다. 이 프로그램은 즉각적인 충돌, 잠시 동안의 운행 및 충돌, 또는 "make demons fly out of your nose"을 포함한 모든 작업을 수행 할 수 있습니다.

귀하의 경우, 보호되지 않은 메모리에 쓰기로 인해 일부 데이터 (예 : 나중에 다른 세그먼트의 주소를 일으키는 다른 개체의 주소)가 덮어 쓰기되는 것으로 의심됩니다. 그러나 이것은 순수한 추측이며 추구할만한 가치가 없습니다.

개체가 생성되지 않았습니다. 바이너리 파일에는 몇 바이트 만 있습니다. read()은 제공된 주소로 복사합니다.이 주소는 그 용도로 예약되지 않았습니다. UB를 피하려면 개체를 만들려면 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 

참고를 들어, 충돌이 발생할 수있다.

그러나,이 모든 마이크로 최적화 어쨌든 무의미 (당신이 newdelete많은 호출을 피할 수 있다면 그것은 일을 유일한 가치). 그것으로 당신의 시간을 낭비하지 마십시오.

+0

나는 아직도 그것을 얻지 않는다. 문제가 "삭제 된 객체의 주소를 전달하는"경우 read()가 호출 될 때 오류가 발생하므로 저장된 값이 표시되지 않습니다. 그리고 바이너리 파일에서 객체를 읽을 때 이미 생성 된 객체가 아닙니까? – hecate

+0

@hecate 정의되지 않은 동작에 오신 것을 환영합니다 !! [대안 버전] (http://coliru.stacked-crooked.com/a/788ceb42e90b4904). 범위를 사용하여 객체가 할당 해제 된 시점을 제어하고 RAII를 조회 할 수 있습니다. –