Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use smart pointer for auto clean-up?

I'm making a simple logging class with a pointer to either a std::ofstream or std::cerr.

Is there any simple way to use a smart pointer for auto clean-up regardless of which stream is used?

The code must compile on clang++, g++, and VS2013.

Code

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

Attempts

std::unique_ptr

I can use std::unique_ptr for the file like so:

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

Trying this with std::cout warns me about a deleted function (assuming that's the constructor.

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

std::shared_ptr

Because std::unique_ptr is only for owning things, and std::cout shouldn't be owned, I thought I'd try std::shared_ptr

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

It gives me the same deleted constructor error. p = &std::cout complains about a type mismatch, so it's also not working.

like image 467
Ben Avatar asked Jul 31 '15 16:07

Ben


People also ask

When should I use smart pointer?

Smart pointers should be preferred over raw pointers. If you feel you need to use pointers (first consider if you really do), you would normally want to use a smart pointer as this can alleviate many of the problems with raw pointers, mainly forgetting to delete the object and leaking memory.

Should I always use a smart pointer?

So, a smart pointer is only needed, when you use new or other means of dynamic memory allocation. In my opinion, you should prefer to allocate variables on the stack, so when refactoring code (to C++11), you should always ask yourself, if this new is needed, or could be replaced with an object on the stack.

Should I use unique_ptr or shared_ptr?

Use unique_ptr when you want to have single ownership(Exclusive) of the resource. Only one unique_ptr can point to one resource. Since there can be one unique_ptr for single resource its not possible to copy one unique_ptr to another. A shared_ptr is a container for raw pointers.

Do smart pointers delete themselves?

Concept of the C++11 Smart Pointers Smart pointers are class objects that behave like built-in pointers but also manage objects that you create with new so that you don't have to worry about when and whether to delete them - the smart pointers automatically delete the managed object for you at the appropriate time.


2 Answers

You can use a shared_ptr with a deleter that does not delete anything in the case of cerr and just a normally constructed shared_ptr in the case of ofstream

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;
    }
};
like image 172
ex-bart Avatar answered Oct 15 '22 01:10

ex-bart


I would just have two pointers, one smart and one raw.

The raw pointer is always used to refer to the stream. The smart pointer is just used for clean-up if needed.

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;
    }
};
like image 5
Chris Drew Avatar answered Oct 15 '22 02:10

Chris Drew