如何在不复制和保留源std :: string对象的情况下获取std :: string char数据的所有权? (我想在不同类型之间使用移动语义。)
基本上我想做一些与此相同的事情:
{
std::string s(“Possibly very long user string”);
const char* mine = s.c_str();
// 'mine' will be passed along,
pass(mine);
//Made-up call
s.release_data();
// 's' should not release data, but it should properly destroy itself otherwise.
}
为了澄清,我确实需要摆脱std :: string:继续前进。该代码处理字符串和二进制数据,并应以相同的格式处理它。我确实想要来自std :: string的数据,因为它来自另一个与std :: string一起使用的代码层。
为了提供更多透视图,我想要这样做:例如,我有一个异步套接字包装器,应该能够从用户获取std :: string和二进制数据进行写入。两个“API”写入版本(将std :: string或行二进制数据)内部解析为相同(二进制)写入。我需要避免任何复制,因为字符串可能很长。
WriteId write( std::unique_ptr< std::string > strToWrite )
{
// Convert std::string data to contiguous byte storage
// that will be further passed along to other
// functions (also with the moving semantics).
// strToWrite.c_str() would be a solution to my problem
// if I could tell strToWrite to simply give up its
// ownership. Is there a way?
unique_ptr<std::vector<char> > dataToWrite= ??
//
scheduleWrite( dataToWrite );
}
void scheduledWrite( std::unique_ptr< std::vecor<char> > data)
{
…
}
在这个例子中,std :: unique_ptr来说明所有权转移:任何其他具有相同语义的方法对我来说都没问题。
我想知道这个特定情况的解决方案(使用std :: string char buffer)以及字符串,流和类似常规的这类问题:在字符串,流,std容器和缓冲区类型之间接近移动缓冲区的技巧
我还要感谢有关在不同API /类型之间传递缓冲区数据而不进行复制的C ++设计方法和特定技术的提示和链接。我提到但不使用流,因为我对这个主题感到不稳定。
答案 0 :(得分:9)
如何在不复制和保留源std :: string对象的情况下获取std :: string char数据的所有权? (我想使用移动语义但在不同类型之间)
你不能安全地做到这一点。
对于特定的实现,在某些情况下,你可以做一些非常糟糕的事情,比如使用别名来修改字符串中的私有成员变量,以欺骗字符串使其认为它不再拥有缓冲区。但即使你愿意尝试这一点,也不会一直有效。例如。考虑小字符串优化,其中字符串没有指向保存数据的外部缓冲区的指针,数据在字符串对象本身内。
如果您想避免复制,可以考虑将接口更改为scheduledWrite。一种可能性是:
template<typename Container>
void scheduledWrite(Container data)
{
// requires data[i], data.size(), and &data[n] == &data[0] + n for n [0,size)
…
}
// move resources from object owned by a unique_ptr
WriteId write( std::unique_ptr< std::vector<char> > vecToWrite)
{
scheduleWrite(std::move(*vecToWrite));
}
WriteId write( std::unique_ptr< std::string > strToWrite)
{
scheduleWrite(std::move(*strToWrite));
}
// move resources from object passed by value (callers also have to take care to avoid copies)
WriteId write(std::string strToWrite)
{
scheduleWrite(std::move(strToWrite));
}
// assume ownership of raw pointer
// requires data to have been allocated with new char[]
WriteId write(char const *data,size_t size) // you could also accept an allocator or deallocation function and make ptr_adapter deal with it
{
struct ptr_adapter {
std::unique_ptr<char const []> ptr;
size_t m_size;
char const &operator[] (size_t i) { return ptr[i]; }
size_t size() { return m_size; }
};
scheduleWrite(ptr_adapter{data,size});
}
答案 1 :(得分:2)
此类使用move语义和shared_ptr获取字符串的所有权:
struct charbuffer
{
charbuffer()
{}
charbuffer(size_t n, char c)
: _data(std::make_shared<std::string>(n, c))
{}
explicit charbuffer(std::string&& str)
: _data(std::make_shared<std::string>(str))
{}
charbuffer(const charbuffer& other)
: _data(other._data)
{}
charbuffer(charbuffer&& other)
{
swap(other);
}
charbuffer& operator=(charbuffer other)
{
swap(other);
return *this;
}
void swap(charbuffer& other)
{
using std::swap;
swap(_data, other._data);
}
char& operator[](int i)
{
return (*_data)[i];
}
char operator[](int i) const
{
return (*_data)[i];
}
size_t size() const
{
return _data->size();
}
bool valid() const
{
return _data;
}
private:
std::shared_ptr<std::string> _data;
};
使用示例:
std::string s("possibly very long user string");
charbuffer cb(std::move(s)); // s is empty now
// use charbuffer...
答案 2 :(得分:1)
您可以使用多态来解决此问题。基类型是统一数据缓冲区实现的接口。那么你将有两个派生类。一个用于std::string
作为源,另一个用于您自己的数据表示。
struct MyData {
virtual void * data () = 0;
virtual const void * data () const = 0;
virtual unsigned len () const = 0;
virtual ~MyData () {}
};
struct MyStringData : public MyData {
std::string data_src_;
//...
};
struct MyBufferData : public MyData {
MyBuffer data_src_;
//...
};