我想写一个像这样的
的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.)
我在Bjarne Stroustrup一书中找到了operator FILE*() { return p; }
行。
但是哪条运营商在这条线路上超负荷? (表示为:标记A)
答案 0 :(得分: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 :(得分:1)
此运算符“operator FILE *(){return p;}”是一个强制转换运算符。只要将File_handle对象传递给只需要FILE *参数的函数,就会隐式调用它。即使此函数从未使用显式获取File_handle对象的版本重载,它也将透明地处理该对象,因为将调用转换操作符用于将类型File_handle转换为类型FILE *。现在,您可以将File_handle对象与仅知道如何处理FILE *参数的API一起使用。透明转换为您的代码提供干净,熟悉且易于阅读的外观。但有时编译器决定选择与您认为应该使用的转换函数不同的转换函数。关于编译器如何针对某些特定情况选择转换函数,有一些细微的规则。如果很多这些隐式转换都是在幕后发生的,那么很难调试出来的问题 - 即使是类的编写者也可能不知道内部实际发生了什么。
至于“&lt;&lt;&lt;操作者...
我将创建一个实现类似ostream的示例类“&lt;&lt;”运算符,但我会完全忽略标准的C ++库 - 它只使用原始C ++和C运行时。我将其遗漏,以便将基础C ++与库隔离开来。我不是在鼓吹或反对制作这样的课程 - 我只想清楚地展示这种基本技术。
在File_handle的类定义块中,我添加了几个File_handle :: operator&lt;&lt;(x)的重叠 - 您需要为每个基本类型创建不同的版本,或者至少为您希望处理的每种类型。诀窍是滥用移位运算符的语义:这些重载都返回对File_handle对象本身的引用。因此,当您在左侧启动一个带有File_handle实例的表达式时,每次“&lt;&lt;”在进行求值时,会调用其中一个重载(根据右侧参数的类型进行选择),然后计算对同一File_handle对象的引用。所以“&lt;&lt;”紧跟第一次评估后的运算符将再次使用初始类实例作为其左侧参数,但右侧参数将是下一个操作数,即initail评估右侧的下一个操作数。这就是无限数量的操作数可以与“&lt;&lt;”链接在一起的方式,所有操作数都是从您的类的单个实例从左到右处理的。
赦免紧凑的风格。我正在努力保持整个事情。另外,当我做这个时,我提到了一个真正的工作类定义,但我实际上没有测试甚至尝试编译它...... 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)); }
};
这个例子省略了一些基本类型(比如short,unsigned short,long long ......),但是你明白了。
此外,还可以添加一些特殊且有趣的重载。
如果有人将另一个File_handle对象粘贴到链中会怎么样?如果除了写入之外还添加了一种支持读取的方法,可能会将读取对象文件中的数据复制到写入对象的文件中。
当FILE *被投入链中时,也许你可以做一些特别的事。
你可能想要恢复标准库 - 如果const char *重载没有像你想要的那样处理std :: string对象,也许它可以单独处理: File_handle&安培;运算符&lt;&lt; (const std :: string&amp; x){...... return * this; }
您还可以添加一个重载,它将处理所有未由任何其他重载处理的类型。模板重载只会处理任何明确类型的版本都无法处理的类型。因此,在类定义块中,您可以添加:
template <typename T>
File_handle& operator << (const T& x) {
return operator << ("what the hell is this crap?");
}
(至少它避免了编译错误)
顺便说一下,在示例中,当重载返回运算符&lt;&lt;(?)时,它们只是根据传入的新类型将工作传递给不同的重载。
祝你好运。答案 2 :(得分:0)
在要包含在打开的文件流中的任何其他类中,只需在using namespace声明后包括:extern ofstream 文件的名称,并记住包含#include。
应该这样做。