开发者

Move value from local stack to heap? (C++)

开发者 https://www.devze.com 2023-04-07 19:09 出处:网络
How can I take an object-as-value result from a method call and place it on the heap? For instance: The Qt QImage::scaled开发者_StackOverflow中文版ToWidth method returns a copy of the QImage object.

How can I take an object-as-value result from a method call and place it on the heap?

For instance:

The Qt QImage::scaled开发者_StackOverflow中文版ToWidth method returns a copy of the QImage object.

Right now I'm doing:

QImage *new_img_on_heap = new QImage(old_imgage_on_heap->scaledToWidth(2000));

Is this the only way? Seems like it's going through the trouble of making a 3rd whole new object when I already have a perfect good one on the stack.

The reason I want to put it on the heap is because the real QImage is large, and I want it to outlive the lifetime of the current method. I was intending to stuff a pointer to it in a field of my class.

I know that QImage has some sort of implicit data sharing, but I'm not exactly clear on how it works under the hood. Plus, I wanted to know a general solution should I ever need to use objects not as well designed as Qt's.


An object is identified by its address. If you want it at another address, you have to construct a new one; you can't move objects. (Even with C++11, the new “move” semantics don't actually move an object; they provide an optimized way of moving its value, if you know that you won't need the value from where you're moving it.)


Firstly, you should prefer the stack to the heap in most cases.

Secondly, when you allocate objects on the heap you should wrap the raw pointer in some managed object which is on the stack. If for nothing else, do it for exception safety.

Thirdly, Qt objects usually do exactly that i.e. there's no advantage in writing new QImage or new QMap<int> etc.

Nevertheless the right way to move a non-heap object onto the heap is to say

new ObjectType (old_instance)

If you do this with a temporary object, such as

new ObjectType (create_object ())

then it will probably elide the extra copy (link broken for me -- cached)


Since the method

QImage QImage::scaledToWidth ( int width, Qt::TransformationMode mode = Qt::FastTransformation ) const

does not return a QImage address (i.e. QImage& ....) than I think you are out of luck. You are stuck with a copy. Unless you want to recompile the QT libraries to change that function signature so that it does not return a copy.

What is so bad about having that object on the stack anyways?


You can try following trick. Idea is to put your value to some structure and construct structure in heap. In this case copy elision works. No copies or moves are performed. Unfortunately it does not work on VS2017. But it works in G++ 8+ and probably in VS2019.

#include <memory>

struct C{

    C() = default;
    C( C&& ) = delete;
    C( C const& ) = delete;

    C& operator=( C&& ) = delete;
    C& operator=( C const& ) = delete;
};

C foo() {
    return C {};
}

struct X {
    C c;

    X() 
        : c (foo()) // here is how trick works
    {
    }
};

int main() {
    auto x = std::make_unique<X>();
}


The premise of this question/ask is incorrect in that it assumes that the image data for QImage has the same lifetime of the QImage object itself.

The reason I want to put it on the heap is because the real QImage is large, and I want it to outlive the lifetime of the current method. I was intending to stuff a pointer to it in a field of my class.

This statement is not correct as it would be a VERY BAD thing for something like QImage, and very quickly fail by popping "stack" limits. It would also make assignments very slow!

Rather, QImage, like many C++ objects that deal with large and/or external data, is effectively a wrapper around internally managed memory of a different lifetime. This can be seen in the QImage source code; QImage contains QImageData, and QImageData malloc's the internal buffer independently of the original QImage's lifetime.

As the bulk of the data is "outside" of QImage, there is little reason to prefer new QImage instead of using QImage and auto lifetimes for sake of the (incorrectly perceived) storage duration of the image data; QImage implements copy and move sanely*, so that all underlying image data (and underlying allocation of such) is shared.

*In QImage's copy ctor the 'ref count' of the QImageData is briefly extended until the destruction of the previous object, while in the move the QImageData's lifetime could be directly transferred (in comment, missing from that source?). Regardless, neither case actually makes a copy of the underlying image data which would be expensive.

So, to answer the question: new X(non_ptr_X), and let the properly written constructors/design do their thing, which is an implementation detail; and hopefully one of agreeable performance characteristics. YMMV.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号