2012-02-12 8 views
1

외부 파일 스트림을 사용하는 방법 난 내가 클래스 외부 파일 스트림을 사용하는 방법을 모르겠어요이file_handle 클래스 - 내가 클래스

#include <stdio.h> 
#include <iostream> 
#include <string> 

//=========================================================================== 
class File_handle { 
    FILE* p; 
    public: 
    File_handle(const char* pp, const char* r) { 
    p = fopen(pp, r); 
    } 
    File_handle(const std::string& s, const char* r) { 
    p = fopen(s.c_str(), r); 
    } 
    ~File_handle() { 
    if (p) 
    fclose(p); 
    } 
    // what is this ????? i found it in stroustrups c++ on page 389 
    // ---> Mark A 
    operator FILE*() { return p;} 


    // I have tried something like this, but without any success 
    // ---> Mark B 
    //friend std::ostream& operator<<(std::ostream&, const File_handle&); 
}; 

//=========================================================================== 
int main() { 
    const std::string filename("test.txt"); 
    File_handle fh(filename, "w"); 
    // doesn't work 
    // fh << "hello\n"; 
    // *fh << "hello\n"; 
    // this works now 
    File_handle fA("A.txt", "w"); 
    fprintf(fA, "say hello to A.txt"); 
    File_handle fB("B.txt", "w"); 
    fputs("say hello to B.txt", fB); 
    return 0; 
} 

같은 file_handle 클래스를 작성합니다. operator<< 오버로드로 위의 코드 예제에서 볼 수있는 방법을 시도했습니다. (위의 코드에서 B로 표시)

그리고 Bjarne Stroustrup의 책에서 operator FILE*() { return p; } 줄을 발견했습니다. 그러나이 줄에서 어떤 연산자가 오버로드 되었습니까? (표시 : A 표시)

+5

당신은 마술을 무료로받지 못합니다. 당신이 한 모든 것은 파일을 닫는 RAII를 제공하기 위해'FILE *'을 감싸는 것입니다 (당신이 할 수도있는 것은'unique_ptr'로 할 수 있습니다.) (http://codereview.stackexchange.com/q/4679). 왜 ''을 사용하지 않으시겠습니까? –

+0

'FILE *'? 이것은 C++입니다. 그것을 좋아하십시오! –

+1

이 예제는 Keynote의 슬라이드에서 발견되었습니다 - Bjarne Stroustrup : C++ 11 Style. – fvdb

답변

1

File_handle 클래스 대신 std::fstream을 사용해야합니다.

그러나 추가 동작을 지정해야하는 경우 클래스를 std::fstream에서 파생시킬 수 있습니다. 그래서 이런 종류의 해결책은 다음과 같습니다 :

#include <iostream> 
#include <fstream> 

class File_handle : public std::fstream 
{ 
public: 
    File_handle(const char* filename, _Openmode o) { open(filename, o); } 
    ~File_handle() { close(); } 
}; 

int main() 
{ 
    File_handle fh("test.txt", std::ios::out); 
    fh << "aa"; 
    return 0; 
} 

희망이 있습니다.

1

이 연산자 인 "연산자 FILE *() {return p;}"는 형 변환 연산자입니다. File_handle 오브젝트가 FILE * 인수 만 예상하는 함수에 전달 될 때마다 내재적으로 호출됩니다. 이 함수는 명시 적으로 File_handle 객체를 사용하는 버전으로 오버로드되지 않더라도 캐스트 연산자가 File_handle 유형에서 FILE * 유형으로 변환하는 데 사용되기 때문에 객체를 투명하게 처리합니다. 이제 FILE * 인수를 처리하는 방법 만 알고있는 API로 File_handle 객체를 사용할 수 있습니다. 투명 변환은 코드를 깨끗하고 친숙하며 읽기 쉬운 모양으로 만듭니다. 그러나 때로는 컴파일러가 사용해야하는 것과 다른 변환 함수를 선택하기도합니다. 컴파일러가 특정 상황에서 변환 함수를 선택하는 방법에 대한 미묘한 규칙이 있습니다. 배후에서 이러한 암시 적 변환이 많이 발생하면 디버깅하기가 어려운 문제가 발생할 수 있습니다. 심지어 클래스의 작성자도 실제로 내부에서 진행되고있는 작업을 인식하지 못할 수 있습니다.

은 "< <"연산자에 대해서는

...

가 나는 ostream에 같은 "< <"연산자를 구현하는 예제 클래스를 만들거야,하지만 난 완전히 표준 C++ 라이브러리를 떠날거야 - - 그냥 원시 C++과 C 런타임을 사용합니다. 라이브러리에서 근본적인 C++을 분리하기 위해 그것을 제외하고 있습니다. 이런 수업을 만드는 것에 찬성하거나 반대하지 않을 것입니다. 단지이 기본 기술을 분명히 보여주고 싶습니다. File_handle의

내부 File_handle의 클래스 정의 블록, 내가 추가 한 여러 overlads :: 연산자 < < (X) - 각 기본 타입에 대해 서로 다른 버전을 만들 필요가, 또는 적어도 각 유형은 당신이 처리 할 것으로 예상된다. 트릭은 시프트 연산자의 의미를 악용하는 것입니다. 이러한 오버로드는 모두 File_handle 객체 자체에 대한 참조를 반환합니다. 그래서 왼쪽의 File_handle 인스턴스로 표현식을 시작하면 "< <"이 평가 될 때마다 과부하 중 하나가 호출되고 (오른쪽 인수의 유형에 따라 선택됨) 동일한 File_handle 객체에 대한 참조. 따라서 첫 번째 평가 바로 다음에 나오는 "< <"연산자는 왼쪽 인수에 초기 클래스 인스턴스를 다시 사용하지만 오른쪽 인수는 다음 피연산자, 즉 initail 평가 오른쪽에있는 다음 피연산자가됩니다. 이것은 "< <"과 함께 무한 개수의 피연산자를 묶을 수있는 방법으로, 클래스의 단일 인스턴스에 의해 왼쪽에서 오른쪽으로 모두 처리됩니다.

실용적인 스타일. 나는 모든 것을보기 위해 노력하고있다. 짧은처럼 (또한, 나는이를 만들 때 클래스 정의를 작업을 실제라고하지만 실제로

class File_handle { 
     FILE* fp; 
    public: 
     File_handle(const char* name, const char* mode) : fp(fopen(name, mode)) {} 
     ~File_handle() { Close(); } 
     operator FILE*() { return fp; } 
     void Close() { 
      if(fp) { fclose(fp); fp = 0; } 
     } 

     File_handle& operator<< (const char* s) { fputs(s, fp); return *this; } 
     File_handle& operator<< (char c) { fputc(c, fp); return *this; } 
     File_handle& operator<< (int x)  { fprintf(fp,"%d",x); return *this; } 
     File_handle& operator<< (unisgned x){ fprintf(fp,"%d",x); return *this; } 
     File_handle& operator<< (double x) { fprintf(fp,"%f",x); return *this; } 
     File_handle& operator<< (float x) { return operator<<(double(x)); } 
     File_handle& operator<< (void*ptr) { fprintf(fp,"%p",p); return *this; } 
     File_handle& operator<< (bool x) { return operator<<(int(x)); } 
    }; 

이 예는 몇 가지 기본 유형을 생략 ... 테스트하거나 심지어 그것을 컴파일하려고하지 않았다 unsigned short, long long ...)하지만 아이디어를 얻습니다.

또한 특수하고 흥미로운 오버로드를 추가 할 수 있습니다.

누군가 다른 File_handle 개체를 체인에 붙이면 어떻게 될까요? 쓰기 외에도 읽기를 지원하는 방법을 추가하면 읽기 전용 파일의 데이터를 쓰기 객체의 파일로 복사 할 수 있습니다.

아마도 파일 *이 체인에 던져지면 특별한 것을 할 수 있습니다.

당신은 표준 라이브러리를 돌아 오게 할 수 있습니다 - 당신이 원하는처럼 과부하 표준 : : 문자열 개체를 처리하지 않습니다 * CONST 문자가, 어쩌면 개별적으로 취급받을 수있는 경우 : File_handle & 운영자 < < (const를 표준 :: 문자열 & x) {...... return * this; }

다른 오버로드로 처리되지 않는 모든 유형을 처리하는 오버로드를 추가 할 수도 있습니다. 템플릿 오버로드는 명시 적으로 입력 된 버전이 처리 할 수없는 유형 만 처리합니다. 그래서 클래스 정의 블록 내부에, 당신은 추가 할 수 있습니다 : 오버로드가 돌아올 때

template <typename T> 
    File_handle& operator << (const T& x) { 
     return operator << ("what the hell is this crap?"); 
    } 

, 예에서 그런데

, (적어도 그것은 컴파일 오류를 피할 수) 연산자 < <, 그들은 (?) 전달되는 새로운 유형에 따라 다른 오버로드로 작업을 전달하는 것입니다.

행운을 빈다.

0

열려있는 파일 스트림에 포함시키려는 다른 클래스에서는 use namespace 선언 다음에 다음을 포함 시키십시오. extern ofstream 의 extern 문자열과 #include를 포함해야합니다.

그렇게해야합니다.