在 C++ 11 之前, 假设有这么一个 FileHandler
类, 实现如下
#include <iostream>
#include <cstdio> // for FILE*
#include <vector>
class FileHandler {
private:
FILE* file;
FileHandler(const FileHandler&);
FileHandler& operator=(const FileHandler&);
public:
FileHandler(const char* filename, const char* mode) : file(std::fopen(filename, mode)) {
if (!file) {
std::perror("Failed to open file");
throw std::runtime_error("File open error");
}
}
~FileHandler() {
if (file) {
std::fclose(file);
}
}
void write(const char* data) {
if (file) {
std::fputs(data, file);
}
}
void flush() {
if (file) {
std::fflush(file);
}
}
};
int main() {
try {
FileHandler fh("test.txt", "w");
fh.write("Hello, World!\n");
fh.flush();
// std::vector<FileHandler> fileHandlers;
// fileHandlers.push_back(fh); // 此处会编译出错
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl;
}
return 0;
}
这个类是没办法直接放入标准容器(如 std::vector、std::list 等)统一管理的, 因为标准容器要求其元素必须实现拷贝构造函数
方案 一 : 实现 拷贝构造函数
#include <iostream>
#include <cstdio> // for FILE*
#include <cstring> // for std::strcpy
#include <vector>
class FileHandler {
private:
FILE* file;
char filename[256];
char mode[4];
public:
FileHandler(const char* filename, const char* mode) : file(std::fopen(filename, mode)) {
if (!file) {
std::perror("Failed to open file");
throw std::runtime_error("File open error");
}
std::strcpy(this->filename, filename);
std::strcpy(this->mode, mode);
}
FileHandler(const FileHandler& other) : file(NULL) {
file = std::fopen(other.filename, other.mode);
if (!file) {
std::perror("Failed to open file in copy constructor");
throw std::runtime_error("File open error in copy constructor");
}
std::strcpy(filename, other.filename);
std::strcpy(mode, other.mode);
}
~FileHandler() {
if (file) {
std::fclose(file);
}
}
void write(const char* data) {
if (file) {
std::fputs(data, file);
}
}
void flush() {
if (file) {
std::fflush(file);
}
}
};
int main() {
try {
std::vector<FileHandler> fileHandlers;
fileHandlers.push_back(FileHandler("test.txt", "a"));
for(std::vector<FileHandler>::iterator iter_t = fileHandlers.begin(); iter_t != fileHandlers.end(); ++iter_t) {
iter_t->write("Hello, World!\n");
iter_t->flush();
}
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl;
}
return 0;
}
方案 一
有以下问题
- 由于
FILE*
并没有提供自动同步机制, 所以多个对象不能共享同一个文件句柄, 否则会导致不可预料的行为;方案 一
的做法是每次拷贝都会打开一个新的文件句柄,不但增加了不必要的开销,而且无法保持原对象的状态,尤其是在文件操作中,可能会导致文件内容丢失或覆盖. std::vector
在存储 FileHandler 对象时 或 重新分配内存时,会触发拷贝构造,拷贝后的对象会创建新的文件句柄,导致文件状态丢失或重复打开。- 在拷贝构造过程中,如果发生异常(如
std::fopen
失败),程序可能会处于不一致的状态。
方案 二 : std::vector
直接存放类指针
#include <iostream>
#include <vector>
int main() {
try {
std::vector<FileHandler*> handlers;
handlers.push_back(new FileHandler("test1.txt", "w"));
handlers.push_back(new FileHandler("test2.txt", "w"));
handlers[0]->write("File 1 content.\n");
handlers[1]->write("File 2 content.\n");
for (size_t i = 0; i < handlers.size(); ++i) {
delete handlers[i];
}
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl;
}
return 0;
}
方案 二
有以下问题
- 必须手动释放指针,否则会造成内存泄漏。
- 如果发生异常且未正确捕获,可能导致未释放的内存泄漏。