将派生类unique_ptr分配给基类unique_ptr

pik*_*chu 3 c++ oop unique-ptr c++11

我创建了一个从std :: istream派生的自定义istream,当文件是压缩文件时使用自定义streambuf,否则使用std :: filebuf.

#mystream.h
class my_stream: public istream {
  public:
     explicit my_stream(const std::string &path);
  private:       
     std::unique_ptr<std::streambuf> b_;
}

#mystream.cpp
my_stream::my_stream(const std::string &path) :std::istream(nullptr) {
    if(path.substr(path.length()-6, path.length())==".gzip"){
        b_ = std::make_unique<gzipbuf>(path); //gzipbuf is derived from std::streambuf
    } 
    else {
        std::unique_ptr<std::filebuf> fb;
        fb->open(path.c_str(), std::ios::in);
        b_ = fb;
    }
    this->init(b_.get());
}
Run Code Online (Sandbox Code Playgroud)

我能够在一个地方将派生类unique_ptr分配给基类unique_ptr

b_ = std::make_unique<gzipbuf>(path);
Run Code Online (Sandbox Code Playgroud)

但不是另一个

b_ = fb;
Run Code Online (Sandbox Code Playgroud)

它说候选函数不可行:没有已知的从'unique_ptr >>转换为'unique_ptr,default_delete >>'的第一个参数operator =(unique_ptr && __u)noexcept

Chr*_*rew 7

首先,在此之后

std::unique_ptr<std::filebuf> fb;
Run Code Online (Sandbox Code Playgroud)

fb实际上并没有指向任何东西,它只是一个空的,unique_ptr所以你在这里调用未定义的行为:

fb->open(path.c_str(), std::ios::in);
Run Code Online (Sandbox Code Playgroud)

要解决此问题,只需将行更改为:

auto fb = std::make_unique<std::filebuf>();
Run Code Online (Sandbox Code Playgroud)

关于你得到的错误,如果允许这条线

b_ = fb;
Run Code Online (Sandbox Code Playgroud)

然后事后双方b_并fb会指向同一个对象.这是不允许的unique_ptr.资源可以由一个资源拥有,只有一个资源unique_ptr.一种解决方案是将所有权从使用转移fb到b_使用std::move:

b_ = std::move(fb)
Run Code Online (Sandbox Code Playgroud)

然后fb不再拥有任何东西.

就个人而言,我喜欢尽可能初始化构造函数初始化程序列表中的成员变量,并将streambuf为单独的函数提取创建它们,以便这样做:

std::unique_ptr<std::streambuf> createStream(const std::string &path) {
    if(path.substr(path.length()-5, path.length())==".gzip"){  // I think you meant 5 here!
        return std::make_unique<gzipbuf>(path); 
    }
    auto fb = std::make_unique<std::filebuf>();
    fb->open(path.c_str(), std::ios::in);
    return fb;
}
Run Code Online (Sandbox Code Playgroud)

那么构造函数my_stream可以是:

my_stream::my_stream(const std::string &path) : std::istream(nullptr),
  b_(createStream(path)) {
    this->init(b_.get());
}
Run Code Online (Sandbox Code Playgroud)