使用移动的std :: string的.data()成员不适用于小的字符串?

tcb

为什么以下程序打印出垃圾而不是hello有趣的是,如果我替换hellohello how are you,那么它将打印出来hello how are you

#include <string>
#include <iostream>

class Buffer
{
public:
    Buffer(std::string s):
        _raw(const_cast<char*>(s.data())),
        _buffer(std::move(s))
    {    
    }

    void Print()
    {
        std::cout << _raw;
    }

private:
    char* _raw;
    std::string _buffer;
};    

int main()
{   
    Buffer b("hello");
    b.Print();
}
霍华德·辛南特

从你的问题,你意味着类不变Buffer类不变是被假定为一个类的数据成员之间的关系始终是真的。在您的情况下,隐式不变是:

assert(_raw == _buffer.data());

乔阿希姆·皮勒博格(Joachim Pileborg)正确地描述了为什么在您的Buffer(std::string s)构造函数中未维护此不变式(已赞成)。

事实证明,保持此不变性是令人惊讶的棘手。因此,我的第一个建议是重新设计Buffer,以便不再需要此不变式。最简单的方法是_raw在需要时随时进行计算,而不是存储它。例如:

void Print()
{
    std::cout << _buffer.data();
}

话虽如此,如果您真的需要存储_raw 维护此不变式:

assert(_raw == _buffer.data());

以下是您需要走的路...

Buffer(std::string s)
    : _buffer(std::move(s))
    , _raw(const_cast<char*>(_buffer.data()))
{
}

重新排序初始化,以便首先_buffer通过移入进行构造,然后指向_buffer不要指向s该构造函数完成后将被破坏的本地对象。

这里的一个非常微妙的一点是,尽管事实上我已经对构造函数中的初始化列表进行了重新排序,实际上尚未真正对实际构造进行重新排序。为此,我必须对数据成员声明列表重新排序:

private:
    std::string _buffer;
    char* _raw;

顺序由此顺序决定的,而不是构造函数中初始化列表的顺序决定了首先构造哪个成员。如果尝试将构造函数初始化列表的顺序与成员实际构造的顺序不同,则某些启用了警告的编译器会警告您。

现在,对于任何字符串输入,您的程序都将按预期运行。但是,我们才刚刚开始。Buffer仍然是越野车,因为您的不变式仍然没有得到维护。证明这一点的最佳方法是在中声明您的不变式~Buffer()

~Buffer()
{
    assert(_raw == _buffer.data());
}

就目前而言(并且没有~Buffer()我刚刚建议的用户声明),编译器将为您提供另外四个签名:

Buffer(const Buffer&) = default;
Buffer& operator=(const Buffer&) = default;
Buffer(Buffer&&) = default;
Buffer& operator=(Buffer&&) = default;

并且编译器会为这些签名中的每一个破坏您的不变式。如果~Buffer()按照我的建议进行添加,则编译器将不会提供move成员,但仍会提供copy成员,并且仍然会出错(尽管该行为已被弃用)。即使析构函数确实禁止了复制成员(就像将来的标准一样),代码仍然很危险,因为在维护过程中,有人可能会像这样“优化”您的代码:

#ifndef NDEBUG
    ~Buffer()
    {
        assert(_raw == _buffer.data());
    }
#endif

在这种情况下,编译器将提供错误的副本并以发布模式移动成员。

要修复该代码,您必须在每次构造重新建立您的类不变式_buffer,否则指向它的突出指针可能会失效。例如:

Buffer(const Buffer& b)
    : _buffer(b._buffer)
    , _raw(const_cast<char*>(_buffer.data()))
{
}

Buffer& operator=(const Buffer& b)
{
    if (this != &b)
    {
        _buffer = b._buffer;
        _raw = const_cast<char*>(_buffer.data());
    }
    return *this;
}

如果将来添加任何可能使之失效的成员,则_buffer.data()必须记住要重置_raw例如,set_string(std::string)成员函数将需要这种处理。

尽管您没有直接提出疑问,但您的问题却暗示了类设计中的一个非常重要的点:要知道您的类不变式以及如何维护它们。推论:尽量减少必须手动维护的不变数。并测试您的不变式实际上是否得到维护。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

选择字符串不适用于使用输出字符串的管道对象

来自分类Dev

为什么std :: uppercase不适用于字符串?

来自分类Dev

使用isEqualToString进行比较不适用于iPhone中的Url编码的字符串

来自分类Dev

C中使用无效指针的通用堆栈不适用于字符串

来自分类Dev

Bash case语句不适用于使用jq的json字符串值

来自分类Dev

通过使用Nginx React Web App路由不适用于查询字符串

来自分类Dev

C中使用无效指针的通用堆栈不适用于字符串

来自分类Dev

适用于iOS的Google登录:错误“无法使用索引类型为“字符串”的下标类型为[[String:AnyObject]”的值”

来自分类Dev

我在php中使用ucwords函数,但不适用于“ Tmtraders”或“ Rjkumar”之类的字符串。

来自分类Dev

性状std :: convert :: From <String>不适用于hyper :: body :: Body

来自分类Dev

在C ++中仅使用std :: string成员来反转没有循环的字符串

来自分类Dev

计数字符串中的字符不适用于制表符('\ t')。我使用错误的方法吗?

来自分类Dev

是否有可能从std :: string中获取内存(就像字符串移动ctor那样)?

来自分类Dev

JSON.parse() 不适用于变量类型字符串,但如果直接使用,则可以使用其内容

来自分类Dev

C字符串作为模板非类型参数可在gcc 6.3中使用,但不适用于Visual Studio 2017(x64为19.16.27027.1)

来自分类Dev

使用std:string名称的类中的字符串

来自分类Dev

如果条件不适用于String

来自分类Dev

格式字符串既不适用于SSAS计算得出的成员,也不适用于度量

来自分类Dev

LocalStorage 不适用于移动设备?

来自分类Dev

Ajax 不适用于移动设备

来自分类Dev

POpen适用于字符串,但不能使用变量

来自分类Dev

网格项目点击事件不适用于像移动设备这样的小屏幕

来自分类Dev

用于检查字符串中的所有字符是否唯一的 Java 算法不适用于 Hashmap?使用地图运算符有更好的解决方案吗?

来自分类Dev

std::bind 不适用于参考?

来自分类Dev

使用std :: search搜索std :: string中的子字符串

来自分类Dev

C ++使用固定数量的空字符串实例化std :: vector <std :: string>

来自分类Dev

使用非拉丁字符时,自动完成功能不适用于某些移动浏览器

来自分类Dev

搜索和替换-。sub(replacement,string [,count = 0])-不适用于特殊字符

来自分类Dev

为什么使用std :: string和std :: string&类似的函数初始化字符串?

Related 相关文章

  1. 1

    选择字符串不适用于使用输出字符串的管道对象

  2. 2

    为什么std :: uppercase不适用于字符串?

  3. 3

    使用isEqualToString进行比较不适用于iPhone中的Url编码的字符串

  4. 4

    C中使用无效指针的通用堆栈不适用于字符串

  5. 5

    Bash case语句不适用于使用jq的json字符串值

  6. 6

    通过使用Nginx React Web App路由不适用于查询字符串

  7. 7

    C中使用无效指针的通用堆栈不适用于字符串

  8. 8

    适用于iOS的Google登录:错误“无法使用索引类型为“字符串”的下标类型为[[String:AnyObject]”的值”

  9. 9

    我在php中使用ucwords函数,但不适用于“ Tmtraders”或“ Rjkumar”之类的字符串。

  10. 10

    性状std :: convert :: From <String>不适用于hyper :: body :: Body

  11. 11

    在C ++中仅使用std :: string成员来反转没有循环的字符串

  12. 12

    计数字符串中的字符不适用于制表符('\ t')。我使用错误的方法吗?

  13. 13

    是否有可能从std :: string中获取内存(就像字符串移动ctor那样)?

  14. 14

    JSON.parse() 不适用于变量类型字符串,但如果直接使用,则可以使用其内容

  15. 15

    C字符串作为模板非类型参数可在gcc 6.3中使用,但不适用于Visual Studio 2017(x64为19.16.27027.1)

  16. 16

    使用std:string名称的类中的字符串

  17. 17

    如果条件不适用于String

  18. 18

    格式字符串既不适用于SSAS计算得出的成员,也不适用于度量

  19. 19

    LocalStorage 不适用于移动设备?

  20. 20

    Ajax 不适用于移动设备

  21. 21

    POpen适用于字符串,但不能使用变量

  22. 22

    网格项目点击事件不适用于像移动设备这样的小屏幕

  23. 23

    用于检查字符串中的所有字符是否唯一的 Java 算法不适用于 Hashmap?使用地图运算符有更好的解决方案吗?

  24. 24

    std::bind 不适用于参考?

  25. 25

    使用std :: search搜索std :: string中的子字符串

  26. 26

    C ++使用固定数量的空字符串实例化std :: vector <std :: string>

  27. 27

    使用非拉丁字符时,自动完成功能不适用于某些移动浏览器

  28. 28

    搜索和替换-。sub(replacement,string [,count = 0])-不适用于特殊字符

  29. 29

    为什么使用std :: string和std :: string&类似的函数初始化字符串?

热门标签

归档