2012-04-13 3 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; 
} 

Edit: Поскольку некоторые люди любопытно сообщение об ошибке, что я получаю с этим текущий код, здесь:

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

Ваша проблема в том, что вы вообще не сообщаете никаких конкретных сообщений об ошибках. Как мы можем, возможно, идентифицировать их еще? – Puppy

+0

@DeadMG Я предположил, что было бы ясно, что есть проблемы с кодом, так как я заметил, что я не уверен, что это правильный способ использовать интеллектуальные указатели и перенести семантику. Я хотел бы, чтобы код делал больше, чем компилировал; Я бы хотел, чтобы это было правильно и идиоматично. Тем не менее, я обновил вопрос с сообщением об ошибке. –

+0

Ошибка, которую вы получаете, заключается в том, что вы пытались скопировать уникальный_ptr, вам нужно использовать std :: move. Вы могли бы использовать shared_ptr и объявили свой собственный deallocator, но векторное решение намного чище. – pstrjds

ответ

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

невыполненным конструктор шаг просто переместить конструкты каждый элемент (и аналогичен для дефолте уступки перемещения).

При возврате d от LoadFile происходит неявное перемещение, которое будет связываться с неявным конструктором перемещения по умолчанию.

Использование 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) –

+0

@ReiMiyasaka: Да, синтаксис '= default' - это способ явно запросить, что в C++ 98/03 вы бы назвали неявным образом созданным специальным членом , Работает по умолчанию ctor, копирует ctor, присваивает копии, перемещает ctor, переводит назначение и dtor. Но это может быть не реализовано для вас (я не знаком с VC++ 11). Эти функции были стандартизированы в конце игры, поэтому понятно, что у вашего компилятора их еще нет. Синтаксис «= delete» примерно эквивалентен объявлению его частным и не определяющим. Я не уверен, что происходит с вашим явным движением ctor. –

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

+0

В этом случае мне даже не понадобилась бы структура FileData, чтобы ответить на половину моего вопроса. Благодарю. Я считаю, что производительность не так уж плоха при изменении размера вектора? –

+1

@Rei: в данном примере он не будет изменяться. Поскольку он изначально пуст, он больше похож на «размер»: P и он не медленнее, чем выделять память самостоятельно. – Puppy

-1

Как об использовании зЬй :: строки в качестве буфера. Это все поведение, которое вы хотите:

  • подсчета ссылок, а не скопированы
  • исчезает, как только выходит за рамки
  • провести произвольное количество произвольных байт

Люди будут вниз голосом это потому, что его а не оригинальное предполагаемое использование строки; возможно получить класс (или обернуть его) и назовите его «буфер»

+0

Я думаю, что это сработает, но опять же, это не помогает мне понять, как использовать интеллектуальные указатели в других подобных ситуациях. –

+0

i dont know unique_ptr (я использую boost), но unique_ptr выглядит неправильно для меня, обычно у массивов ptrs есть свой класс. У меня был бы uniq_ptr . В основном это должно работать – pm100

+0

Когда значение 'std :: string' засчитывалось?Или это зависит от реализации в разных компиляторах? –

Смежные вопросы