2012-02-12 3 views
1

Я хочу написать file_handle класс, как этот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; 
} 

Я не знаю, как я могу использовать файловый поток вне класса. Я пробовал это с перегрузкой operator<<, как вы можете видеть в приведенном выше примере кода. (Обозначается как: отметка B в приведенном выше коде.)

И я нашел строку operator FILE*() { return p; } в книге Бьярна Страуступа. Но какой оператор перегружен в этой строке? (Обозначается как: знак A)

+5

Вы не получаете магию бесплатно. Все, что вы сделали, это «FILE *», чтобы предоставить RAII для закрытия файла (что вы также можете сделать [с помощью 'unique_ptr'] (http://codereview.stackexchange.com/q/4679)). Почему вы не используете ''? –

+0

'FILE *'? Это C++. Действуйте так! –

+1

Я нашел этот пример на слайде из Keynote - Bjarne Stroustrup: C++ 11 Style. – fvdb

ответ

1

Вы должны использовать std::fstream вместо своего класса File_handle.

Но если вам нужно указать некоторые дополнительные действия, вы можете получить свой класс из 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_handle с API-интерфейсами, которые знают только, как обрабатывать аргументы FILE *. Прозрачное преобразование дает вашему коду чистый, знакомый и удобный для чтения вид. Но иногда компилятор решает выбрать другую функцию преобразования, чем тот, который, по вашему мнению, должен использоваться. Существуют тонкие правила suume о том, как компилятор выбирает функцию преобразования для определенной ситуации. Если многие из этих неявных преобразований происходят за кулисами, могут возникнуть проблемы, которые очень трудно отлаживать - даже писатель класса может не знать, что происходит внутри.

Что касается «< <» оператора ...

Я сделаю пример класса, который реализует «< <» оператора ostream-как, но я полностью уйти из стандартной библиотеки C++ - - он просто использует raw C++ и C Runtime. Я оставляю это, чтобы изолировать фундаментальный C++ от библиотеки. Я не выступаю за или против создания такого класса - я просто хочу четко продемонстрировать эту основную технику.

В блоке определения класса Inside File_handle я добавил несколько надписей File_handle :: operator < (x) - вам нужно сделать другую версию для каждого базового типа или, по крайней мере, каждого типа, который вы собираетесь обрабатывать. Трюк заключается в злоупотреблении семантикой оператора сдвига: эти перегрузки возвращают ссылку на объект File_handle. Поэтому, когда вы начинаете выражение с экземпляром File_handle с левой стороны, каждый раз, когда вычисляется «< », вызывается одна из ваших перегрузок (выбирается в соответствии с типом аргумента правой стороны), а затем оценивается ссылку на тот же объект File_handle. Поэтому оператор «< <» сразу после первой оценки снова будет использовать исходный экземпляр класса для его левого аргумента, но правым аргументом будет следующий операнд, следующий справа от оценки инициализации. Таким образом, неограниченное количество операндов можно связать вместе с «< <», все обработанные слева направо одним экземпляром вашего класса.

Прощение компактного стиля. Я пытаюсь держать все это в поле зрения. Кроме того, я сослался на реальное, рабочее определение класса, когда я сделал это, но я на самом деле не проверить или даже попытаться скомпилировать его ...

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 в цепочку? Если вы добавите способ поддержки чтения в дополнение к записи, возможно, это скопирует данные из файла объекта чтения в файл записывающего объекта.

Возможно, вы могли бы сделать что-то особенное, когда FILE * будет выброшен в цепь.

Вы можете вернуть стандартную библиотеку - если 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

В любом другом классе, который вы хотите включить в открытый поток файлов, просто включите после использования объявления пространства имен следующее: extern ofstream имя файла, а также не забудьте включить #include.

, который должен это сделать.

Смежные вопросы