只谈C++11新特性 - 右值引用 & 移动语义(1)

在 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;
}

方案 一 有以下问题

  1. 由于 FILE* 并没有提供自动同步机制, 所以多个对象不能共享同一个文件句柄, 否则会导致不可预料的行为; 方案 一 的做法是每次拷贝都会打开一个新的文件句柄,不但增加了不必要的开销,而且无法保持原对象的状态,尤其是在文件操作中,可能会导致文件内容丢失或覆盖.
  2. std::vector 在存储 FileHandler 对象时 或 重新分配内存时,会触发拷贝构造,拷贝后的对象会创建新的文件句柄,导致文件状态丢失或重复打开。
  3. 在拷贝构造过程中,如果发生异常(如 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;
}

方案 二 有以下问题

  1. 必须手动释放指针,否则会造成内存泄漏。
  2. 如果发生异常且未正确捕获,可能导致未释放的内存泄漏。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值