C++ 正在尝试编写可以从std::string中移动语义的string类

C++ 正在尝试编写可以从std::string中移动语义的string类,c++,c++11,dynamic-memory-allocation,stdstring,move-semantics,C++,C++11,Dynamic Memory Allocation,Stdstring,Move Semantics,我写我自己的弦乐课,真的只是为了学习和巩固一些知识。除了我想要一个使用std::string的move语义的构造函数外,我所有的东西都在工作 在我的构造函数中,我需要复制并清空std::string数据指针和其他东西,它需要保持为空但有效的状态,而不删除字符串所指向的数据,我如何做到这一点 到目前为止,我有这个 class String { private: char* mpData; unsigned int mLength; public: String( std::string&&

我写我自己的弦乐课,真的只是为了学习和巩固一些知识。除了我想要一个使用std::string的move语义的构造函数外,我所有的东西都在工作

在我的构造函数中,我需要复制并清空std::string数据指针和其他东西,它需要保持为空但有效的状态,而不删除字符串所指向的数据,我如何做到这一点

到目前为止,我有这个

class String
{
private:
char* mpData;
unsigned int mLength;
public:
String( std::string&& str)
    :mpData(nullptr), mLength(0)
    {
    // need to copy the memory pointer from std::string to this->mpData

    // need to null out the std::string memory pointer
    //str.clear();  // can't use clear because it deletes the memory
    }
~String()
{
delete[] mpData;
mLength = 0;
}

没有办法做到这一点。
std::string的实现由实现定义。每个实现都是不同的


此外,不能保证字符串将包含在动态分配的数组中。一些
std::string
实现执行小字符串优化,其中小字符串存储在
std::string
对象本身中。

我将问题解释为“我能使移动构造函数产生正确的行为吗”而不是“我能使移动构造函数以最佳速度运行吗?”

如果问题严格地说是“是否有一种从std::string窃取内部内存的可移植方法”,那么答案是“否,因为公共API中没有提供“转移内存所有权”操作”


以下引用提供了“移动构造函数”的良好摘要

C++0x引入了一种称为“右值引用”的新机制, 除此之外,它允许我们通过函数检测右值参数 超载。我们所要做的就是用右值编写一个构造函数 参考参数。在这个构造器中,我们可以做任何我们想做的事情 只要我们让源代码保持在某种有效状态,就可以使用源代码

基于此描述,在我看来,您可以实现“移动语义”构造函数(或“移动构造函数”),而无需实际窃取内部数据缓冲区。 一个示例实现:

String( std::string&& str)
    :mpData(new char[str.length()]), mLength(str.length())
    {
    for ( int i=0; i<mLength; i++ ) mpData[i] = str[i];
    }
String(std::String&&str)
:mpData(新字符[str.length()]),mllength(str.length())
{

对于(int i=0;i std::string)。

下面的实现完成了请求,但存在一定风险

有关此方法的说明:

  • 它使用std::string来管理分配的内存。在我看来,像这样分层分配是一个好主意,因为它减少了单个类试图完成的事情的数量(但是由于使用了指针,这个类仍然存在与编译器生成的复制操作相关联的潜在错误)

  • 我取消了
    delete
    操作,因为它现在由
    allocation
    对象自动执行

  • 如果使用
    mpData
    修改底层数据,它将调用所谓的未定义行为。正如所指出的,它是未定义的,因为标准说它是未定义的。不过,我想知道,是否有实际的实现是
    const char*std::string::data()
    的行为不同于
    T*std::vector::data()
    ——通过它,此类修改将完全合法。通过
    data()进行的修改可能是
    不会反映在对
    分配的后续访问中,但根据中的讨论,假设没有通过
    分配
    对象进行进一步更改,则此类修改似乎不太可能导致不可预测的行为

  • 它真的针对移动语义进行了优化吗?这可能是实现定义的。它也可能取决于传入字符串的实际值。正如我在另一个答案中所指出的,移动构造函数提供了一种优化机制——但它不能保证会发生优化


类字符串
{
私人:
字符*mpData;
无符号整数长度;
字符串分配;
公众:
字符串(std::String&&str)
:mpData(const_cast(str.data())//用于调用UB的cast
,mLength(str.length())
,分配(std::move(str))//这就是神奇发生的地方
{}
};

所以这是不可能的?在不同类型的类之间进行移动语义通常是一个坏主意吗?stl如何在字符串的向量类中管理这种语义?一个
向量
只需要能够从另一个
字符串
中移动构造或移动分配一个
字符串交叉类型移动有点奇怪,但如果您同时控制源类型和目标类型的实现,或者源类型提供了某种方式从中移动(例如,
unique\u ptr
提供了一个
release
成员函数),那么交叉类型移动肯定是可能的我真的很讨厌总结结论“不可能”。C++给了你很多的控制,一个状态分配器至少能为一般的代码< St::Basic字符串> /Cux>案例管理它。它只需要记住分配的块(公平地假设一个代码> Basic字符串S/<代码>没有堆上的多个块),而自定义的
移动
-from只需在
清除
基本字符串之前通知它所有权的更改
@Potatoswatter:现在你不能再从任意字符串移动了。@Xeo这不是一个很实用的解决方案(好吧,它和任何自定义分配器一样糟糕,但没有更糟),但更具建设性的是提及您可以做什么,而不是将其保留在“nope”。如果您不移动动态内存,则只需使用引用即可。此外,您的代码可能需要空终止符,具体取决于类的实现。String(std::String&str)我发现这篇博文与我的答案一致:从你的博文中:如果你只是在你的移动构造函数中实现了一个常规复制,那么你确实满足了这两个约束,所以这样做是正确的;然而,确实有
class String
{
private:
char* mpData;
unsigned int mLength;
std::string allocation;
public:
String( std::string&& str)
    : mpData(const_cast<char*>(str.data())) // cast used to invoke UB
    , mLength(str.length())
    , allocation(std::move(str)) // this is where the magic happens
    {}
};