使用Rvalue引用和Simple Factory

Using Rvalue references and Simple Factory

本文关键字:Simple Factory 引用 Rvalue 使用      更新时间:2023-10-16

我最近了解了std::movervalue引用,想知道我对它们的使用是否既合适又有效。

考虑这个简单的Image类,它简单地存储表示像素的unsigned char值的数组。(注意,事实上,成员比单个字符数组多,但为了可读性,我在这里进行了简化。)

using PixelContainer = std::vector<unsigned char>;
class Image {
public:
    Image(PixelContainer&& pixels) : m_pixels(std::move(pixels)) {};
    Image& operator=(Image&& image) { m_pixels = std::move(image.m_pixels); return *this; };
private:
    PixelContainer m_pixels;
};

现在考虑ImageFactory类的这个静态方法,它接受.png的文件名,并返回一个填充了适当像素值的Image对象。

Image ImageFactory::loadImage(const char* filename) {
    PixelContainer temp_pixels;
    // ... fill pixels from file (details not relevant here)
    Image temp_image(std::move(temp_pixels));
    return temp_image;
}

最后,它们一起用于代码中,如下所示:

Image image = ImageFactory::loadImage("image.png");

我使用rvalue引用来消除复制可能是一个大像素值数组所花费的时间。因为工厂创建的矢量和图像是临时的,所以它们可以四处移动。

我的问题是,我的实现有意义吗?如果它没有根本性的缺陷,它能进一步改进吗?

让我们来了解一下!我添加了一个这样的函数来包含你的最后一段代码:

Image outer() {
    Image image = ImageFactory::loadImage("image.png");
    return image;
}

我还必须为Image添加一个常规的复制构造函数(我使用= default来定义它),因为即使它实际上没有使用,它也需要按值返回。

然后我用Clang-O2-g-S编译,并查看组装。它主要是初始化矢量的代码:调用operator new并设置矢量的内部指针。没有复制,代码看起来相当干净高效。正如您所希望的,outer()的代码与loadImage()的代码几乎相同(后者被内联到前者中,因为我将所有内容都放在一个翻译单元中)。

作为参考,这是我得到的组件:

outer():                             ## @_Z5outerv
    pushq   %rbp
    movq    %rsp, %rbp
    pushq   %rbx
    pushq   %rax
    movq    %rdi, %rbx
    movl    $100000000, %edi        ## imm = 0x5F5E100
    callq   operator new(unsigned long)
    movd    %rax, %xmm0
    movlhps %xmm0, %xmm0            ## xmm0 = xmm0[0,0]
    movq    $-100000000, %rcx       ## imm = 0xFFFFFFFFFA0A1F00
    movq    %rax, %rsi
LBB1_1:                                 ## =>This Inner Loop Header: Depth=1
    testq   %rsi, %rsi
    movl    $0, %edx
    je      LBB1_3
    movb    $0, (%rsi)
    movaps  %xmm0, %xmm1
    punpckhqdq      %xmm1, %xmm1    ## xmm1 = xmm1[1,1]
    movd    %xmm1, %rdx
LBB1_3:                                 ## %_ZNSt3__116allocator_traits...
    incq    %rdx
    movd    %rdx, %xmm1
    punpcklqdq      %xmm1, %xmm0    ## xmm0 = xmm0[0],xmm1[0]
    incq    %rcx
    movq    %rdx, %rsi
    jne     LBB1_1
## BB#4:                                ## %_ZN12ImageFactory9loadImageEPKc.exit
    leaq    100000000(%rax), %rax
    movdqu  %xmm0, (%rbx)
    movq    %rax, 16(%rbx)
    movq    %rbx, %rax
    addq    $8, %rsp
    popq    %rbx
    popq    %rbp
    retq