简体   繁体   中英

C++ initialization of local/global object

I've already encountered many time the case when there's a class, eg LogFile , that has no default constructor, while I want to initialize an instance of it, not on the heap.

Here is an example:

LogFile logfile;

void init(const std::string& path) {
    logfile = LogFile(path);
}

The above code will not compile, since LogFile class has no default constructor.

There are a few workarounds I currently use:

1. Persisting not to make any heap calls manually, storing the object in a list (the list will for sure use the heap, but I won't care about it)

std::list<LogFile> logfile_holder;

void init(const std::string& path) {
    logfile_holder.push_back(LogFile(path));
}

2. Using the heap, with a shared pointer/unique pointer:

std::shared_ptr<LogFile> logfile_ptr;

void init(const std::string& path) {
    logfile_ptr.reset(new LogFile(path));
}

I there a more standard solution for doing such things?

Even something that looks like the list workaround, but with a template class that is designated specifically for this purpose, without the overhead of the list, would be nice.

optional can be an alternative ( std or boost one depending of your version of compiler):

std::optional<LogFile> logfile;

void init(const std::string& path) {
    logfile = LogFile(path);
}

You could use indirection. Use a global pointer, and initialize it with a function local static:

LogFile* logfile_ptr;

void init(const std::string& path) {
    static LogFile logfile(path);
    logfile_ptr = &logfile;
}

For your suggestions, there is little point in using a list (or other data structure) when you could use a smart pointer if you were to use heap.

Do note that in this design based on yours, you need to pay attention to not use the global logfile until it has been initialized. If unsure, it is easy to check if the pointer is null.

A simpler approach would be to simply add the default constructor. That would allow changing the behaviour of Logfile if it is used before the path is set. For example, you could terminate the program and describe the error, or you could silently ignore such bugs if you don't care about premature logging that goes missing. Or you could even store logging into a buffer and flush it as soon as the path is finally initialized.

The usual way is to separate a Logger class and have a Logfile class which are coupled internally and separate responsibilities:

  • The Logger class is instantiated when needed and is used to write text formatted logging messages
  • The Logfile class is responsible for the configuration of the application wide logfile. There will be a static class member representing the std::ostream shared with all the Logger instances.

Here's a sketch:

class Logfile {
    friend class Logger;
    static std::unique_ptr<std::ostream> plogstream_;

public:
    void set_logfile(const std::string& logfile_path) {
    void set_logstream(std::unique_ptr<std::ostream> logstream);
};

Logfile::plogstream_ = std::unique_ptr<std::ostream>(std::cerr,[](void*){});

class Logger {
public:
     operator ostream&()() {
          return *Logfile::plogstream_;
     }
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM