【问题标题】:Is this code from "The C++ Programming Language 4th Edition" Section 19.3.3.1 valid?“C++ 编程语言第 4 版”第 19.3.3.1 节中的这段代码有效吗?
【发布时间】:2018-12-25 11:02:01
【问题描述】:

19.3 节在主要关注运算符重载的章节中介绍了字符串表示,特别是特殊运算符[]->()。它将copy_from()作为辅助函数实现如下:

void String::copy_from(const String &x)
    // make *this a copy of x
{
    if (x.sz <= short_max)
    {
        memcpy(this, &x, sizeof(x);
        ptr = ch;
    }
    else
    {
        ptr = expand(x.ptr, x.sz+1);
        sz = x.sz;
        space = 0;
    }
}

类界面如下所示:

#ifndef STRING_EXERCISE_H
#define STRING_EXERCISE_H

namespace simple_string
{
    class String;
    char *expand(const char *ptr, int n);
}

class String
{
    public:
        String(); // default constructor x{""}
        explicit String(const char *p); // constructor from C-style string

        String(const String &s); // copy constructor
        String &operator=(const String& s); // copy assignment
        String(String &&s) // move constructor
        String &operator=(String &&s) // move assignement

        ~String() // destructor

        char &operator[](int n); // unchecked element access
        char operator[](int n) const;
        char &at(int n); // checked element access
        char at(int n) const;

        String &operator+=(char c) // add char c to the end

        const char *c_str(); // c-style string access
        const char *c_str() const;

        int size() const; // number of elements
        int capacity() const; // elements plus available space

    private:
        static const short short_max = 15;
        int sz;
        char *ptr;
        union
        {
            int space; // unused allocated space
            char ch[short_max+1]; // leave space for terminating 0
        };

        void check(int n) const; // range check
        void copy_from(const String &x);
        void move_from(String &x);
}

#endif

String::copy_from()如何使用memcpy()复制类?我认为复制的类必须是可简单复制的(事实并非如此,因为String 具有用户定义的构造函数、复制操作、移动操作和析构函数)。

【问题讨论】:

  • 复制的类与复制到的类完全相同。您可以在自己的课程中利用实施知识
  • 如果对象不是 TriviallyCopyable,则 memcpy 的行为未指定并且可能未定义。 在这种情况下应该没问题,因为作者会负责复制的内容错误。
  • @NathanOliver 不就是outright undefined,不管上课内容吗?
  • 对不起@Joe,你能详细说明一下吗?我不明白这将如何改变标准说它未定义为 memcpy() 非平凡可复制类型这一事实。
  • @NathanOliver 如果“作者处理了错误复制的内容”是否意味着此代码无效?

标签: c++


【解决方案1】:

String::copy_from()如何使用memcpy()复制类?

intchar 和匿名联合都是可简单复制的。因此,虽然您不能执行Stringmemcpy,但您可以执行其成员memcpy。所有这些,一次。技术上正确的代码是:

memcpy(&this->sz, &x.sz, sizeof(x));

为该对象的成员复制存储的内存范围。这是由标准布局类型的规则保证的。对于标准布局类型,成员按定义顺序存储。因此,如果您从第一个开始,并覆盖所有对象的范围,则应该复制成员。

但是,标准也规定标准布局类型的第一个成员子对象必须与对象本身具有相同的地址:

如果标准布局类对象有任何非静态数据成员,则其地址与其第一个非静态数据成员的地址相同。

这意味着&amp;this-&gt;sz 必须this 相同的地址,并且&amp;x.sz 必须与&amp;x 相同的地址。

所以只要去掉中间人:

memcpy(this, &x, sizeof(x));

这只是因为标准布局类型的规则才允许的。


更大的问题是 copy_from 从不检查 self 分配。 memcpy 不适用于重叠的内存范围。也许operator= 和类似的函数已经检查过了。

【讨论】:

  • 你认为这是复制类成员的好方法吗?
  • 我同意“技巧”有效(请参阅下面我的抨击答案)。但真的有必要吗?他本可以分配大小,重置指针并 memcpy 联合。这比记忆所有内容以便以后重新分配 ptr 更糟糕吗?
  • @alfC:有必要吗?不,简单且易于管理?是的。 C++是低级语言,所以做低级的事情也不是没有道理。分配变量需要命名所有变量(因此,如果添加新变量或更改名称,您必须记住更改它)。
【解决方案2】:

我认为您缺少的是代码显示了一种称为短字符串优化的东西,它允许将字符串存储在作为对象一部分的某个缓冲内存中。

所以,是的,对于短字符串,可以复制整个类,因为这就是对象标识的全部内容。 我不会这样做,但概念是,对于短字符串,您可以逐个元素地复制类的内容。 (并重新分配ptr)。

我会这样做并避免讨论,我认为效率不会降低。

if (x.sz <= short_max)
{
    sz = x.sz;
    ptr = ch;
    memcpy(&ch, &x.ch, sizeof(ch));
}

【讨论】:

  • This post 说它是 UB...
  • @HolyBlackCat,我会说有一个“运行时”定义可以简单复制的含义。如果x.sz &lt;= short_max 则(实例)可以简单地复制,这就是他可以使用memcpy 的原因。话虽如此,我会做不同的事情,我不会评估代码的质量。我试图给 OP 一个实用的答案,让他了解正在做的事情。另外,我手边没有这本书,所以我不知道copy_from 是如何被调用的,大概是在clearing 原始字符串之后(如果它很长的话)。
  • 我同意在实践中它可能没问题。但正式它是UB。 ://
  • 好吧,标准似乎没有说明它是无条件影响未定义的。
  • @DavidTran,我不是外行,但请参阅“如果 objects 不是 TriviallyCopyable,则未指定 memcpy 的行为,可能是 未定义”。 (强调我的。)请注意,它谈论的是“对象”而不是类,而且这个短语是有条件的(“可能是”)意味着它实际上是 UB 的某种运行时条件。
猜你喜欢
  • 2015-01-25
  • 2018-01-23
  • 2016-12-04
  • 2018-06-06
  • 1970-01-01
  • 1970-01-01
  • 2023-03-14
  • 1970-01-01
  • 2011-07-26
相关资源
最近更新 更多