按位掉掉()是个坏主意的例子?

Meh*_*dad 7 c++ swap bit-manipulation

您不应该将对象指针视为OOP语言(包括C++)中原始二进制数据的指针.对象"超过"它们的表示.

因此,例如,swap通过交换字节来处理两个对象是不正确的:

template<class T>
void bad_swap(T &a, T &b)  // Assuming T is the most-derived type of the object
{
    char temp[sizeof(T)];
    memcpy(temp, &a, sizeof(a));
    memcpy(&a, &b, sizeof(b));
    memcpy(&b, temp, sizeof(temp));
}
Run Code Online (Sandbox Code Playgroud)

然而,唯一的情况是,我可以想象这个导致问题的快捷方式是当一个对象包含一个指向自身的指针时,我很少(从不?)在实践中看到它; 但是,也可能是其他情景.

swap如果你执行按位交换,什么是正确会破坏的实际(真实世界)示例?
我可以很容易地用自我指针提出人为的例子,但我想不出任何真实的例子.

Bo *_*son 13

这不是具体的,swap而是一个示例,表明低级别优化可能不值得麻烦.无论如何,编译器经常会想出来.

当然,这是我最喜欢的编译器非常幸运的例子,但无论如何我们不应该认为编译器是愚蠢的,并且我们可以通过一些简单的技巧轻松改进生成的代码.

我的测试代码是 - 构造一个std :: string并复制它.

std::string whatever = "abcdefgh";
std::string whatever2 = whatever;
Run Code Online (Sandbox Code Playgroud)

第一个构造函数看起来像这样

  basic_string(const value_type* _String,
               const allocator_type& _Allocator = allocator_type() ) : _Parent(_Allocator)
  {
     const size_type _StringSize = traits_type::length(_String);

     if (_MySmallStringCapacity < _StringSize)
     {
        _AllocateAndCopy(_String, _StringSize);
     }
     else
     {
        traits_type::copy(_MySmallString._Buffer, _String, _StringSize);

        _SetSmallStringCapacity();
        _SetSize(_StringSize);
     }
  }
Run Code Online (Sandbox Code Playgroud)

生成的代码是

   std::string whatever = "abcdefgh";
000000013FCC30C3  mov         rdx,qword ptr [string "abcdefgh" (13FD07498h)]  
000000013FCC30CA  mov         qword ptr [whatever],rdx  
000000013FCC30D2  mov         byte ptr [rsp+347h],0  
000000013FCC30DA  mov         qword ptr [rsp+348h],8  
000000013FCC30E6  mov         byte ptr [rsp+338h],0  
Run Code Online (Sandbox Code Playgroud)

这里traits_type::copy包含一个调用memcpy,它被优化为整个字符串的单个寄存器副本(仔细选择以适合).编译器还将调用strlen转换为编译时8.

然后我们将其复制到一个新的字符串中.复制构造函数看起来像这样

  basic_string(const basic_string& _String)
     : _Parent(std::allocator_traits<allocator_type>::select_on_container_copy_construction(_String._MyAllocator))
  {
     if (_MySmallStringCapacity < _String.size())
     {
        _AllocateAndCopy(_String);
     }
     else
     {
        traits_type::copy(_MySmallString._Buffer, _String.data(), _String.size());

        _SetSmallStringCapacity();
        _SetSize(_String.size());
     }
  }
Run Code Online (Sandbox Code Playgroud)

并且仅产生4个机器指令:

   std::string whatever2 = whatever;
000000013FCC30EE  mov         qword ptr [whatever2],rdx  
000000013FCC30F6  mov         byte ptr [rsp+6CFh],0  
000000013FCC30FE  mov         qword ptr [rsp+6D0h],8  
000000013FCC310A  mov         byte ptr [rsp+6C0h],0  
Run Code Online (Sandbox Code Playgroud)

请注意,优化器会记住char它们仍然在寄存器中rdx,并且字符串长度必须相同,8.

在看到这样的事情之后,我喜欢信任我的编译器,并避免尝试使用bit fiddling来改进代码.除非分析发现意外的瓶颈,否则它无济于事.

(以MSVC 10和我的std :: string实现为特色)

  • 提供更多信息 - 我不会为需要重新分配版权的开源项目贡献代码,因为我已经正式将我的所有版权重新分配给我现在的雇主(银行,如果律师有很多).为了安全起见,我还避免发布任何更大量的代码,以免因某些法律技术问题而陷入困境. (2认同)

Mar*_*k B 8

我将争辩说,这几乎总是一个主意,除非在特定情况下进行分析并且有更明显和明确的swap性能问题实现.即使在这种情况下,我也只会采用这种方法来直接进行无继承结构,从不用于任何类.你永远不知道什么时候会增加继承可能会破坏整个事情(也可能以真正阴险的方式).

如果你想要一个快速交换实现,或许更好的选择(在适当的情况下)是pimpl该类,然后只是交换实现(再次,这假设没有指向所有者的后向指针,但这很容易包含在类中&impl而不是外部因素).

编辑:这种方法可能出现的问题:

  • 指针回归自我(直接或间接)
  • 如果类包含直接字节副本无意义的任何对象(有效地递归此定义)或通常禁用复制
  • 如果类需要任何类型的锁定来复制
  • 这里很容易意外地传递两种不同的类型(它只需要一个中间函数来隐式地使派生类看起来像父类)然后你交换vptrs(OUCH!)