3

我正在编写自己的弦乐课程,实际上只是为了学习和巩固一些知识。除了我想要一个使用带有 std::string 的移动语义的构造函数之外,我一切正常。

在我的构造函数中,我需要复制 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;
}
4

3 回答 3

7

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

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

于 2012-07-28T04:19:30.220 回答
1

下面的实现完成了所要求的,但有一定的风险。

关于这种方法的注意事项:

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

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

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

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


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
    {}
};
于 2012-09-23T00:39:36.827 回答
-1

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

如果问题是严格的,“是否有一种可移植的方式从 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];
    }

据我了解,移动语义的意义在于,如果您愿意,您可以提高效率。由于传入的对象是瞬态的,因此不需要保留其内容——因此窃取它们是合法的,但不是强制性的。也许,如果您不转移某些基于堆的对象的所有权,那么实现这一点是没有意义的,但它似乎应该是合法的。也许它可以用作垫脚石——你可以尽可能多地窃取有用的东西,即使这不是全部内容。

顺便说一句,这里有一个密切相关的问题其中正在构建相同类型的非标准字符串,并包括 std::string 的移动构造函数。然而,该类的内部结构有所不同,建议 std::string 可能具有对内部移动语义的内置支持(std::string -> std::string)。

于 2012-07-28T05:15:07.583 回答