在堆栈上分配对象(带有副本)或堆(带指针)

时间:2015-07-08 03:14:00

标签: c++

我有一些关于使用堆栈与堆的基本问题。那里有很多答案,但我不确定如何将它与下面的内容联系起来。

我认为通过举例说明更容易解释......

做“更好”

struct Widget3 {
    Widget3(std::shared_ptr<Widget1> widget1, std::shared_ptr<Widget2> widget2)
    : _widget1(std::move(widget1))
    , _widget2(std::move(widget2))
    {
    }

    void doSomething()
    {
        std::cout << _widget1.hello() << _widget2.hello();
    }
private:
    std::shared_ptr<Widget1> _widget1;
    std::shared_ptr<Widget2> _widget2;
};

或者

struct Widget3 {
    Widget3(const Widget1 & widget1, const Widget2 & widget2)
    : _widget1(widget1)
    , _widget2(widget2)
    {
    }

    void doSomething()
    {
        std::cout << _widget1->hello() << _widget2->hello();
    }
private:
    Widget1 _widget1;
    Widget2 _widget2;
};

第二个例子让我们复制Widget1Widget2,但是我们假设我们在程序开始时创建所有这些对象并且不关心成本(性能和内存)复制对象。

使用第二个示例,其中_widget1_widget2在堆栈中是否更快?

2 个答案:

答案 0 :(得分:0)

你的第二种方式更好,因为如果你选择不使用智能指针,就不会有内存泄漏。

但不能保证它会在堆栈上完成。

如果我写了:

Widget1 widget1;
Widget2 widget2;
Widget3 *widget3 = new Widget3(widget1, widget2);

int main(){
    // do something with 'widget3'
    delete widget3;
}
那么呢?

你应该尽可能不使用指针,它只是更安全。但是,如果您的数据有多个句柄,并且您无法通过引用传递(出于任何疯狂的原因),那么请务必使用示例1。

在这种情况下,哪种方法更好。没有哪种方法始终优于另一种方法。

答案 1 :(得分:0)

首先,这两个Widget3实现是不一样的。 Widget3与shared_ptr的第一个实现将简单地移动指针(不会复制Widget1或Widget2)。但是,第二个实现会将Widget1和Widget2复制到成员变量中。

以下是证据:

#include <iostream>
#include <memory>

namespace
{
    std::ostream& os = std::cout;
}

struct Widget1
{
    Widget1()
    {
        os << __FUNCSIG__ << std::endl;
    }
    Widget1( const Widget1& rhs )
    {
        os << __FUNCSIG__ << std::endl;
    }
    Widget1( Widget1&& rhs )
    {
        os << __FUNCSIG__ << std::endl; 
    }
};

struct Widget2
{
    Widget2()
    {
        os << __FUNCSIG__ << std::endl;
    }
    Widget2( const Widget2& rhs )
    {
        os << __FUNCSIG__ << std::endl;
    }
    Widget2( Widget2&& rhs )
    {
        os << __FUNCSIG__ << std::endl;
    }
};

struct Widget3_1 {
    Widget3_1(std::shared_ptr<Widget1> widget1, std::shared_ptr<Widget2> widget2)
    : _widget1(std::move(widget1))
    , _widget2(std::move(widget2))
    {
    }
private:
    std::shared_ptr<Widget1> _widget1;
    std::shared_ptr<Widget2> _widget2;
};


struct Widget3_2 {
    Widget3_2(const Widget1 & widget1, const Widget2 & widget2)
    : _widget1(widget1)
    , _widget2(widget2)
    {
    }
private:
    Widget1 _widget1;
    Widget2 _widget2;
};


int main()
{
    const int repeatCount = 1;
    {
        os << " Widget3_1 : " << std::endl;
        std::shared_ptr<Widget1> widget1( new Widget1 );
        std::shared_ptr<Widget2> widget2( new Widget2 );

        Widget3_1(widget1, widget2);
    }
    {
        os << " Widget3_2 : " << std::endl;
        Widget1 widget1;
        Widget2 widget2;

        Widget3_2(widget1, widget2);
    }

} // int main

输出:

 Widget3_1 :
__thiscall Widget1::Widget1(void)
__thiscall Widget2::Widget2(void)
 Widget3_2 :
__thiscall Widget1::Widget1(void)
__thiscall Widget2::Widget2(void)
__thiscall Widget1::Widget1(const struct Widget1 &)
__thiscall Widget2::Widget2(const struct Widget2 &)

您不仅要担心其他事情,我认为它们之间没有任何区别。随着项目规模的扩大,差异可能会增大,但除了测试之外别无他法,以证明究竟发生了什么。为了说服你,让我们给你一些测试结果。这是在VisualC,Windows 8.1

上测试的
#include <iostream>
#include <string>
#include <memory>
#include <intrin.h>

namespace
{
    std::ostream& os = std::cout;
    typedef decltype(__rdtsc()) ClockType;
    ClockType clock;

    void MeasureBegin()
    {
        clock = __rdtsc();
    }

    ClockType MeasureEnd()
    {
        return __rdtsc() - clock;
    }
}

struct Widget1
{
    Widget1()
        : a(0),b(1),c(2),d(3)
    {
    }
    int a,b,c,d;
};

struct Widget2
{
    Widget2()
        :j ("Hello, World")
    {
    }
    std::string j;
};

struct Widget3 {
    Widget3(const Widget1 & widget1, const Widget2 & widget2)
    : _widget1(widget1)
    , _widget2(widget2)
    {
    }
private:
    Widget1 _widget1;
    Widget2 _widget2;
};

int main()
{
    const int testCount = 10;
    const int repeatCount = 100000;
    ClockType c = 0;

    for( int j = 0; j < testCount; ++j )
    {
        Widget1 widget1;
        Widget2 widget2;

        MeasureBegin();
        for (int i = 0; i < repeatCount; ++i)
        {
            Widget3(widget1, widget2);
        }
        c = MeasureEnd();
        std::cout << " 1 : " << c << std::endl;

        Widget1* pwidget1 = new Widget1();
        Widget2* pwidget2 = new Widget2();

        MeasureBegin();
        for (int i = 0; i < repeatCount; ++i)
        {
            Widget3(*pwidget1, *pwidget2);
        }
        c = MeasureEnd();
        std::cout << " 2 : " <<  c << std::endl;

        delete pwidget1;
        delete pwidget2;
    }
} // int main

输出:

1 : 5030133
2 : 5726116
1 : 4898262
2 : 5915359
1 : 4805982
2 : 4988638
1 : 4758595
2 : 5065566
1 : 5470122
2 : 5312685
1 : 5133652
2 : 5753500
1 : 5475479
2 : 5196111
1 : 5153481
2 : 5678220
1 : 4790526
2 : 4820254
1 : 4836252
2 : 5231158

没有区别。