有没有人知道任何 C ++类,它们不是二元可移动的?
换句话说,您是否知道任何类会导致内存泄漏或未定义的行为?
{ // New scope
// Allocate some memory
unsigned char data1[sizeof(MyClass)];
unsigned char data2[sizeof(MyClass)];
// Create an instance of the class
::new (data1) MyClass();
// Create a pointer and run some class-specific code
MyClass* ptr = (MyClass*) data1;
ptr->MyFunction();
// Binary move everything to data2
::memcpy((void*)data2, (void*)data1, sizeof(MyClass));
::memset((void*)data1, sizeof(MyClass), 0); // for clarification
// Run some code at the new memory location, same object
ptr = (MyClass*) data2;
ptr->MyFunction();
// Run destructor at memory location2
(&*data2)->~MyClass();
} // ... out of scope. No implicit destructors called. No memory leaks.
更新:
二进制可移动的意思是指任何可以二进制移动到内存中另一个位置但仍能正常运行的对象(移动构造函数可以用memcpy()实现)。
当然,任何指向它们的指针都不能移动,这不是问题的一部分。
(澄清)tr1 :: smart_ptr,std :: deque,std :: vector和std :: list以及类可以是二元可移动的,所以不要讨论他们的内部行为重新分配节点时。
(澄清)处理内存分配的类可以是二进制可移动的,因为二进制 move 意味着你在移动后不处理这两个对象,并且析构函数不会被调用两次。
答案 0 :(得分:8)
如果任何其他对象存储指向对象的指针,则对象不再是二进制可移动的(也称为按位可移动)。双链表单元素就是一个很好的例子。如果要重新定位双向链表的元素,则需要调整相邻元素中的指针。因此,如果您尝试仅使用memmove()
来重新定位元素,则列表会中断。
答案 1 :(得分:3)
使用原始内存以避免过早构建的类:
template <class T, size_t N>
class aligned_storage
{
public:
private:
T* mHead;
unsigned char mData[N*sizeof(T) + std::tr1::alignment_of<T>::value - 1];
};
这个问题有两个方面:
mHead
指向对象本身,因此它应该被更改,当然当然,任何类都使用一些订阅机制(观察者模式),因为您需要取消订阅复制的对象并从新对象订阅。
在Move Constructor中使用按位复制与复制构造函数之间没有太大区别......一旦你处理非POD,这是一个坏主意。
答案 2 :(得分:2)
任何流类(*)都有一个内部缓冲区,指针指向该缓冲区中的下一个字符。它们不是二元可移动的。
(*)流类的本质:提供对一些连续数据流的访问,一次访问一部分(char,int,string等)。几乎总是通过一次从数据源(例如文件)中读取一个大块来实现,然后用指针逐步执行它。
答案 3 :(得分:2)
我使用offset_ptr类型来构建 记忆blob中的结构,我 想要从内存中交换 到磁盘或网络,或使用 共享内存。 offset_ptr存储 对象位置“t”为 “this”和“t”之间的距离。在 为了复制偏移指针, 内部偏移量必须调整, 因此它只能被保留 对象它指向它的记忆 相同的金额(情况就是如此) 对于上述记忆blob)。
使用对象并不罕见 位置作为访问的关键 相关资源,特别是如果 资源经常没有被使用 并会增加相当大的开销 如果聚合到对象本身。 例如,如果你想要每个对象 你可能有一个可选的互斥锁 想要组成一个全局哈希表 在哪里映射对象位置 (这)与它相关的互斥锁, 这当然是在什么时候创建的 实际要求。你不能记住这样的 对象,显然。
答案 4 :(得分:2)
智能指针,容器和其他资源管理类(其析构函数负责释放资源)必须通过移动无效。否则,旧对象将释放应该已转移到新资源的资源。
更新:这是原始问题的答案,此问题已被更改为使其无效。
答案 5 :(得分:1)
某些包含可变长度数据的类具有在连续块中分配sizeof(Class) + ExtraDataSize
的分配函数,然后使用placement new
在该空间中创建对象。在这样的方案中,类不需要具有指向额外数据的成员指针,因为它将位于(unsigned char*)this + sizeof(*this)
。
显然,这样的课程不会有公开复制的ctor。理论上你可以二进制移动它,但是分配器只返回Class*
,你不知道需要移动多少额外的字节。
答案 6 :(得分:1)
使C ++对象难以移动的原因不是单个对象在复制时停止工作(在自身内)。该问题涉及相对较小的对象类(如包含指向自身的对象或在某种循环引用集中密切工作的对象)。
C ++对象很难移动,因为:
他们被指向!这是头号问题。除非你找到某种方法来确保在将所有指针移动到将来可能使用的对象之后将会更新,所以根本不考虑移动对象。根据定义,C ++几乎不可能。
在运行时很难确定任何给定对象的实际类(和大小)。你是如何创建任何一般移动过程的,如果你不能确定对象是什么类(如果需要你不能更新嵌入式指针),它真正开始的地方(想想:虚拟继承)和它的大小(说什么你想要的,但是在末尾具有可变大小缓冲区的旧C结构是有效的C ++对象;对于C数组也是如此。
更新(在被OP拒绝投票后未回答他的问题之后)
完全回答完全问题:
有没有人知道任何不是二元可移动的C ++类?
是
根据问题中的定义,每个单个C ++类都不是二进制可移动的。 这简直是合乎逻辑的。
每个C ++类的实例都可以指向它。 班上的作者没有办法阻止它。 此外,在某个时间点,每个类实例肯定都有一个指向它的指针。 所以没有C ++类可以满足条件2: 任何指针指向它们的对象都不能移动。 因此,没有C ++类可以通过这个定义进行二进制移动。
换句话说,您是否知道任何会导致内存泄漏或未定义行为的类? [源代码片段如下]
(忽略问题2不是问题1的事实,换言之“。 差远了。相反,我会回答问题2本身。)
是。许多。
已经提供了几个例子。
更多例子:
链接列表实现为循环双向链表,其中sentinel节点作为链接列表类对象本身的成员嵌入。
执行此代码 - 假设第一个ptr->MyFunction();
创建至少一个节点 - 将在循环删除节点到达旧的前哨节点(现在已归零)的位置时导致未定义的行为。
表单(如在GUI元素中),包含一些控件(指向它们作为成员)并监听它们的事件。
如果第二个ptr->MyFunction();
允许触发事件(例如,通过执行某种Form :: Show方法),当事件处理程序尝试通过已经归零的指针操作控件时,将导致未定义的行为。
如果Class的sizeof == 13(例如,精确值并不重要)并且其第一个成员需要在4字节边界上对齐(例如,它是虚函数表指针)那么它是一个未定义的行为,因为对齐data2的数据不符合Class的要求。在某些平台上并不重要。在其他一些方面,它只会运行得慢得多。在其他人身上会崩溃。
答案 7 :(得分:0)
我认为您很难了解班级实例的确切大小,尤其是考虑虚拟方法。