Бывают ситуации, когда мне нужно передать буфер char*
туда и обратно. Моя идея - создать объект, который может содержать объект, владеющий данными, но также предоставлять данные как char*
для чтения. Поскольку этот объект содержит владельца, утечек памяти нет, потому что владелец уничтожается вместе с объектом, когда он больше не нужен. бывает. На самом деле я знаю, как это исправить, но мой класс как бы заманил меня к этому. Поэтому я считаю то, что я сделал, плохим, и, возможно, есть более безопасный способ сделать это на C ++.
Пожалуйста, взгляните на мой класс, который содержит владельца буфера, а также необработанный указатель на этот буфер. Я использовал GenericObjectHolder
как что-то, что содержит для меня владельца, без того, чтобы мой класс Buffer
параметризовался этим владельцем.
#include <iostream>
#include <string>
#include <memory>
#include <queue>
//The library:
class GenericObjectHolder
{
public:
GenericObjectHolder()
{
}
virtual ~GenericObjectHolder() {
};
};
template <class T, class Holder = GenericObjectHolder>
class Buffer final
{
public:
//Ownership WILL be passed to this object
static Buffer fromOwned(T rawBuffer, size_t size)
{
return Buffer(std::make_unique<T>(rawBuffer), size);
}
//Creates a buffer from an object that holds the buffer
//ownership and saves the object too so it's only destructed
//when this buffer itself is destructed
static Buffer fromObject(T rawBuffer, size_t size, Holder *holder)
{
return Buffer(rawBuffer, std::make_unique<T>(rawBuffer), size, holder);
}
//Allocates a new buffer with a size
static Buffer allocate(size_t size)
{
return Buffer(std::make_unique<T>(new T[size]), size);
}
~Buffer()
{
if (_holder)
delete _holder;
}
virtual T data()
{
return _rawBuffer;
}
virtual size_t size() const
{
return _size;
}
Buffer(T rawBuffer, std::unique_ptr<T> buffer, size_t size)
{
_rawBuffer = rawBuffer;
_buffer = std::move(buffer);
_size = size;
}
Buffer(T rawBuffer, std::unique_ptr<T> buffer, size_t size, Holder *holder)
{
_rawBuffer = rawBuffer;
_buffer = std::move(buffer);
_size = size;
_holder = holder;
}
Buffer(const Buffer &other)
: _size(other._size),
_holder(other._holder),
_buffer(std::make_unique<T>(*other._buffer))
{
}
private:
Holder *_holder;
T _rawBuffer;
std::unique_ptr<T> _buffer;
size_t _size = 0;
};
//Usage:
template <class T>
class MyHolder : public GenericObjectHolder
{
public:
MyHolder(T t) : t(t)
{
}
~MyHolder()
{
}
private:
T t;
};
int main()
{
std::queue<Buffer<const char*, MyHolder<std::string>>> queue;
std::cout << "begin" << std::endl;
{
//This string is going to be deleted, but `MyHolder` will still hold
//its buffer
std::string s("hello");
auto h = new MyHolder<std::string>(s);
auto b = Buffer<const char*, MyHolder<std::string>>::fromObject(s.c_str(),s.size(), h);
queue.emplace(b);
}
{
auto b = queue.front();
//We try to print the buffer from a deleted string, segfault
printf(b.data());
printf("\n");
}
std::cout << "end" << std::endl;
}
Как видите, строка s
копируется внутри объекта держатель, но разрушается сразу после него. Поэтому, когда я пытаюсь получить доступ к необработанному буферу, принадлежащему buffer
, я получаю segfault.
Конечно, я мог бы просто скопировать буфер из строки s
в новый буфер внутри моего объекта, но это ' d быть бесполезным.
Может быть, есть лучший способ сделать это, или, может быть, есть даже что-то готовое в C ++, которое делает то, что мне нужно.
PS: string - это просто пример. На практике я мог иметь дело с любым типом объекта, которому принадлежит буфер char*
.
Живой пример: https://repl.it/repls/IncredibleHomelySdk