2012-04-13 1139 views
6

我写过一个函数,它将字节加载到文件中,并返回包含字节缓冲区和缓冲区长度的FileData结构体。在一个结构体或类中使用智能指针

我想缓冲区一旦被消耗并被抛出作用域就被删除。

我很难让它编译,由于各种铸造错误。另外,我不确定缓冲区是否被正确移动而不是被复制。我不介意FileData结构本身被复制,因为它最多可能是16个字节。

一般来说,你如何使用智能指针作为类/结构域?这甚至是你会做的吗?

这是一个含糊不清的问题,我知道,但由于我在智能指针方面有一些概念上的困难,我希望这个例子能帮助我走向正确的方向。

这里是我到目前为止有:

struct FileData 
{ 
    unique_ptr<char[]> buf; 
    unsigned int len; 
}; 

FileData LoadFile(string filename) 
{ 
    ifstream str; 
    str.open(filename, ios::binary); 

    str.seekg(0, ios::end); 
    auto len = str.tellg(); 
    str.seekg(0, ios::beg); 

    char* buf = new char[len]; 

    str.read(buf, len); 
    str.close(); 

    FileData d = { unique_ptr<char[]>(buf), len }; 

    return d; 
} 

编辑:由于一些人好奇的错误消息,我这个当前的代码得到的,那就是:

error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>' 
+0

您的问题是,您根本不会提供关于错误消息的任何具体细节。我们究竟可以怎样识别他们呢? – Puppy 2012-04-13 00:18:48

+0

@DeadMG我认为应该清楚代码存在问题,因为我确实指出我不确定这是使用智能指针并移动语义的正确方法。我希望代码不仅仅是编译;我希望它是正确和习惯的。尽管如此,我已经用错误信息更新了问题。 – 2012-04-13 00:23:37

+0

你得到的错误是因为你试图复制一个unique_ptr,你必须使用std :: move。您可以使用shared_ptr并声明自己的释放器,但矢量解决方案更清晰。 – pstrjds 2012-04-13 00:26:19

回答

5

你的代码是正确的,除了一个小细节:

struct FileData 
{ 
    unique_ptr<char[]> buf; 
    <del>unsigned int</del> <ins>streamoff</ins> len; 
}; 

它不会编译你的原因是你的编译器尚未实现特别移动成员的自动生成。在完全C++ 11符合标准的编译你的FileData会表现得好像:

struct FileData 
{ 
    unique_ptr<char[]> buf; 
    streamoff len; 

    FileData(FileData&&) = default; 
    FileData& operator=(FileData&&) = default; 
    FileData(const FileData&) = delete; 
    FileData& operator=(const FileData&) = delete; 
    ~FileData() = default; 
}; 

的违约转移构造简单的移动结构的每个成员(以及类似的违约举动分配)。

当从LoadFile返回d时,会发生隐式移动,该移动将绑定到隐式默认移动构造函数。

使用vector<char>string正如其他人所建议的也将工作。但就C++ 11而言,您的代码没有任何问题。

哦,我可能会调整它,像这样:我希望得到尽快拥有尽可能我的资源:

FileData LoadFile(string filename) 
{ 
    ifstream str; 
    str.open(filename, ios::binary); 

    str.seekg(0, ios::end); 
    auto len = str.tellg(); 
    str.seekg(0, ios::beg); 

    FileData d = {unique_ptr<char[]>(new char[len]), len}; 

    str.read(d.buf.get(), d.len); 
    str.close(); 

    return d; 
} 

如果您需要明确定义FileData招成员,它应该看起来像:

struct FileData 
{ 
    unique_ptr<char[]> buf; 
    streamoff len; 

    FileData(FileData&& f) 
     : buf(std::move(f.buf)), 
      len(f.len) 
     { 
      f.len = 0; 
     } 

    FileData& operator=(FileData&& f) 
    { 
     buf = std::move(f.buf); 
     len = f.len; 
     f.len = 0; 
     return *this; 
    } 
}; 

哦,这让我想到另一点。默认移动成员不是,确切地说是正确的,因为他们没有在来源中将len设置为0。如果这是一个错误,这取决于你的文档。 ~FileData()不需要len来反映缓冲区的长度。但其他客户可能会。如果您将移动的FileData定义为没有可靠的len,那么默认的移动成员是好的,否则它们不是。

+0

'FileData(FileData &&)= default;' - 是一种适用于某些编译器的简写语法吗?当我尝试自己创建移动构造函数时,它会抱怨'operator ='无法访问。嗯......(VC++ 11,顺便说一下) – 2012-04-13 00:49:20

+0

@ReiMiyasaka:是的,'= default'语法是一种明确请求C++ 98/03中隐含生成的特殊成员。适用于默认ctor,复制ctor,复制任务,移动ctor,移动任务和dtor。但它可能不会为你实现(我不熟悉VC++ 11)。这些功能在游戏的后期进行了标准化,因此您的编译器可能还没有这些功能是可以理解的。 “= delete”语法大致相当于声明它是私有的,而不是定义它。我不确定你的明确行动是怎么回事。 – 2012-04-13 01:01:08

2

我可能会使用std::vector而不是std:::unique_ptr<char[]>,如果您不介意std::vector正在复制当您返回FileData

struct FileData 
{ 
    vector<char> buf; 
}; 

FileData LoadFile(string filename) 
{ 
    ifstream str; 
    str.open(filename, ios::binary); 

    str.seekg(0, ios::end); 
    auto len = str.tellg(); 
    str.seekg(0, ios::beg); 

    FileData d; 
    d.buf.resize(len); 

    str.read(&(d.buf)[0], len); 
    str.close(); 

    return d; 
} 

另外,避免了复制,主叫方可以在FileData传递作为函数参数,而不是返回值:

struct FileData 
{ 
    vector<char> buf; 
}; 

void LoadFile(string filename, FileData &data) 
{ 
    ifstream str; 
    str.open(filename, ios::binary); 

    str.seekg(0, ios::end); 
    auto len = str.tellg(); 
    str.seekg(0, ios::beg); 

    data.buf.resize(len); 

    str.read(&(data.buf)[0], len); 
    str.close(); 
} 
+0

这不会导致文件数据“数组”本身被复制,或者矢量是否实现了C++ 11移动语义?不要试图争论,只是好奇。 – pstrjds 2012-04-13 00:19:24

+0

在这种情况下,我甚至不需要FileData结构,所以答案是我的问题的一半。谢谢。我认为在调整矢量大小的情况下性能不算太差? – 2012-04-13 00:19:54

+1

@Rei:它不会在给定的例子中调整大小。因为它最初是空的,它更像“大小”:P,它不会比自己分配内存慢。 – Puppy 2012-04-13 00:21:04

-1

如何使用的std :: string作为缓冲。它希望所有的行为:

  • 引用计数,而不是复制
  • 消失,一旦超出范围
  • 持有

人们会下来投票,是因为任意字节任意数量的其不是弦的原始使用意图;也许派生类(或把它包),并把它称为“缓冲”

+0

我认为那会奏效,但是它又一次并没有帮助我弄清楚在其他类似情况下如何使用智能指针。 – 2012-04-13 00:29:31

+0

我不知道unique_ptr(我使用boost),但unique_ptr 看起来不对我,通常数组ptrs有他们自己的类。我会有uniq_ptr 。基本上它应该工作 – pm100 2012-04-13 00:37:37

+0

什么时候'std :: string'成为引用计数?或者是在不同编译器中实现的具体实现? – 2012-04-13 05:04:05