如何使用智能指针进行自动清理

How to use smart pointer for auto clean-up?

本文关键字:何使用 智能 指针      更新时间:2023-10-16

我正在制作一个简单的日志类,其中包含指向std::ofstreamstd::cerr的指针。

是否有任何简单的方法来使用智能指针进行自动清理,而不管使用哪个流?

代码必须在clang++, g++和VS2013上编译。

#include <iostream>
#include <fstream>
#include <string>
class Logger {
private:
    std::ostream * output_stream{ nullptr };
    bool using_file{ false };
public:
    Logger()
    {
        output_stream = &std::cerr;
        using_file = false;
    }
    Logger(std::string file)
    {
        output_stream = new std::ofstream(file);
        using_file = true;
    }
    ~Logger()
    {
        if (using_file)
        {
            delete output_stream;
        }
    }
    template<typename T>
    void log(T info)
    {
        *output_stream << info << std::endl;
    }
};
class tmp {
    int i{ 4 };
    friend std::ostream & operator<<(std::ostream &os, const tmp& p);
};
std::ostream &operator<<(std::ostream &os, const tmp& p)
{
    return os << p.i;
}
int main()
{
    tmp t;
    Logger logger;
    logger.log(t);
    system("pause");
    return 0;
}

尝试

std::unique_ptr

我可以对文件使用std::unique_ptr,如下所示:

std::unique_ptr<std::ostream> p;
p = std::make_unique<std::ofstream>("file.txt");
*p << "hi there" << std::endl;

std::cout尝试这个警告我关于一个被删除的函数(假设它是构造函数)。

std::unique_ptr<std::ostream> p2;
p2 = std::make_unique<std::ostream>(std::cout);
*p2 << "hey" << std::endl;

std::shared_ptr

因为std::unique_ptr只用于拥有东西,而std::cout不应该被拥有,我想我应该试试std::shared_ptr

std::shared_ptr<std::ostream> p;
p = std::make_shared<std::ostream>(std::cout);
*p << "hola" << std::endl;

它给出了相同的已删除构造函数错误。p = &std::cout抱怨类型不匹配,所以它也不能工作

cerr的情况下,可以使用shared_ptr和一个不删除任何内容的delete,而在ofstream的情况下,可以使用一个正常构造的shared_ptr

class Logger {
private:
    std::shared_ptr<std::ostream> output_stream{ nullptr };
public:
    Logger() :
        output_stream(&std::cerr, [](std::ostream*){})
    { }
    Logger(std::string file) :
        output_stream(std::make_shared<std::ofstream>(file))
    { }
    // default destructor is OK
    template<typename T>
    void log(T info)
    {
        *output_stream << info << std::endl;
    }
};

我将只有两个指针,一个智能和一个原始。

原始指针总是用来指向流。如果需要,智能指针只是用于清理。

class Logger {
private:
    std::unique_ptr<std::ofstream> file_stream;
    std:ostream *stream;
public:
    Logger() : stream(&std::cerr) {}
    Logger(const std::string& file)
    : file_stream(std::make_unique<std::ofstream>(file)), stream(file_stream.get()){}
    template<typename T>
    void log(T info) {
        *stream << info << std::endl;
    }
};

我倾向于避免让对象"拥有"这些东西的情况。在我没有太多选择的时候,我最终解决了"shouldDelete"标志或回调。

class Logger {
public:
    Logger(std::ofstream *outputStream, bool deleteOutputStream)
        : outputStream(outputStream), deleteOutputStream(deleteOutputStream)
    {  }
    ~Logger()
    {
        if (deleteOutputStream) delete outputStream;
    }
};
Logger logger(&std::cout, false);
class Logger {
public:
    typedef std::function<void(std::ostream*)> Deleter;
    typedef std::unique_ptr<std::ostream, Deleter> OStreamPointer;
    Logger(OStreamPointer &&outputStream)
        : outputStream(std::move(outputStream))
    {  }
    ~Logger() { }
private:
    OStreamPointer outputStream;
};
Logger logger(Logger::OStreamPointer(
    &std::cout,
    [](std::ostream*) {})); //no-op callback

在不应该删除智能指针的情况下,你可以通过在析构函数(和其他地方)释放智能指针来实现这一点,但在我看来,这是不值得的。

相反,我建议简单地使用两个指针:一个用于需要管理的流,另一个用于不需要管理的流:

class Logger {
private:
    std::ostream * unmanaged_stream{ nullptr };
    std::unique_ptr<std::ostream> managed_stream{ nullptr };
    bool using_file{ false };
    std::ostream& output_stream()
    {
        return using_file ? *managed_stream : *unmanaged_stream;
    }
public:
    Logger()
    : unmanaged_stream{&std::cerr},
      using_file{false}
    {
    }
    Logger(const std::string& file)
    : managed_stream{std::make_unique<std::ofstream>(file)},
      using_file{true}
    {
    }
    template<typename T>
    void log(T info)
    {
        output_stream() << info << std::endl;
    }
};

如果节省空间是一个优先级,你可以把它们放在联合中,但是你必须显式地调用析构函数和位置new来定义活动成员,这同样是更麻烦的,可能不值得。