【问题标题】:Using std::memcpy to object of non-trivially copyable type将 std::memcpy 用于非平凡可复制类型的对象
【发布时间】:2014-11-19 18:31:00
【问题描述】:

标准定义我们可以通过以下方式使用 std::memcpy int:

对于任何可平凡复制的类型 T,如果指向 T 的两个指针指向 不同的 T 对象 obj1 和 obj2,其中 obj1 和 obj2 都不是 基类子对象,如果构成 obj1 的底层字节 (1.7) 是 复制到 obj2 中,obj2 将随后保持与 obj1 相同的值。

如果我们将该函数应用于非平凡可复制类型的对象,我们可能会遇到什么潜在问题?下面的代码就像它适用于普通可复制类型一样工作:

#include <iostream>
#include <cstring>

using std::cout;
using std::endl;

struct X
{
    int a = 6;
    X(){ }
    X(const X&)
    {
        cout << "X()" << endl;
    }
};

X a;
X b;
int main()
{
    a.a = 10;
    std::memcpy(&b, &a, sizeof(X));
    cout << b.a << endl; //10
}

DEMO

【问题讨论】:

  • 明显 UB 是显而易见的。例如,考虑memcpy'ing std::vector
  • 嗯,是的,它可以工作,因为 struct X 可以简单地复制。例如,如果结构的成员包括任何类型的堆分配内存,那么您就会遇到问题。好吧,在一个真正的程序中。不在您的测试中。
  • @Wlerin X 不可轻易复制。
  • @T.C.嗯。它有一个什么也不做的复制构造函数。这可能足以在技术上 使其不易复制,但类的实际内容可以逐个成员地复制而不会出现问题。在 C++11 之前,b = a 的结果也将与 memcpy 的结果相同。无论哪种方式,这个示例程序都不是一个很好的例子。
  • 您的实际问题是什么?你想让我们向你解释什么是未定义的行为吗?还是您只是想确认这实际上是未定义的行为?

标签: c++ types


【解决方案1】:

你问:

如果我们将该函数应用于非平凡可复制类型的对象,我们可能会遇到什么潜在问题?

这是一个非常简单的示例,说明了将std::memcpy 用于非平凡可复制类型的对象的问题。

#include <cstring>

struct A
{
   A(int size) : size_(size), data_(new int[size]) {}
   ~A() { delete [] data_; }

   // The copy constructor and the copy assignment operator need
   // to be implemented for the class too. They have been omitted
   // to keep the code here minimal.

   int size_;
   int* data_;
};

int main()
{
   A a1(10);
   A a2(20);
   std::memcpy(&a1, &a2, sizeof(A));

   // When we return from the function, the original data_ of a1
   // is a memory leak. The data_ of a2 is deleted twice.

   return 0;
}

【讨论】:

  • 确实,将无效指针传递给操作员 delete 会产生 UB。清晰的例子,谢谢。
  • @DmitryFucintv,不客气。很高兴我能回答你的问题。
【解决方案2】:

考虑这个程序:

#include <memory>

int main() {
    std::shared_pointer<int> x(new int);

    {
        std::shared_pointer<int> y;
        memcpy((void*)&y, (void*)&x, sizeof(x));
    }

    *x = 5;
}

因为我们使用memcpy 而不是赋值运算符将x 复制到y,所以引用计数没有得到更新。因此,在该块的末尾,将调用 y 的析构函数。它发现它的引用计数为 1,这意味着它是唯一指向堆分配整数的 shared_pointer 实例。所以它会删除它。

main 的最后一行可能会出现段错误,因为x 指向一个已被删除的对象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-17
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多