在堆栈(带有副本)或堆(带有指针)上分配对象
allocate objects on the stack (with copies) or heap (with pointers)
我有一些关于使用堆栈和堆的基本问题。有很多答案,但我不确定如何将其与以下内容联系起来。
我认为用例子来解释比较容易。
这样做"更好"吗
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;
};
第二个示例导致我们复制Widget1
和Widget2
,但假设我们在程序开始时一次性创建所有这些对象,并且不关心复制对象的成本(性能和内存)。
是否以某种方式更快地使用第二个例子,其中_widget1
和_widget2
在堆栈上?
第二种方法更好,因为如果您选择不使用智能指针,则没有内存泄漏的机会。
不能保证所有操作都在堆栈上完成。
如果我写:
Widget1 widget1;
Widget2 widget2;
Widget3 *widget3 = new Widget3(widget1, widget2);
int main(){
// do something with 'widget3'
delete widget3;
}
那么呢?
你应该尽可能不使用指针,这样更安全。但是,如果你的数据有多个句柄,并且你不能通过引用传递(无论出于什么疯狂的原因),那么无论如何,请使用示例1。
关键是在这种情况下哪种方法更好。 不可能总是优于其他方法。
首先,这两个Widget3实现是不一样的。使用shared_ptr的Widget3的第一个实现将简单地移动指针(不会复制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
没有区别。
相关文章:
- 有没有一种方法可以使用placement new将堆叠对象分配给分配的内存
- C++ 如何在将新对象分配给另一个对象时创建新对象
- 为什么我可以在不重载 "=" 运算符的情况下将一个对象分配给另一个对象?
- 为对象分配整数.输出将是什么?
- C++ 使用枚举类对象分配 std::map 值
- 如何在C++中为增加但记住删除先前对象的对象分配唯一标识符
- 我重载了 << 和 = 运算符。为什么当我将一个对象分配给另一个对象并尝试打印它时,我会被打印出来?
- 为什么将两个对象分配给另一个对象后,两个对象不一样?
- 有没有办法在同名类 (c++) 中为对象分配一个指针变量
- 如何使用函数(而不是构造函数)将派生类对象分配给基类指针
- 是否可以使用 malloc 为类对象分配内存?
- 将对象分配给数组C++
- 我们可以在cpp中为对象分配函数吗
- 为模板参数类型中的新对象分配内存
- 我无法将对象分配给对象数组
- 对象分配-成员函数的使用无效错误
- 将派生对象分配给函数内的基类指针
- Arduino ESP32如何将BLEUUID对象分配到字符串中
- Rapidjson将密钥值从一个文档对象分配到另一个文档对象
- 奇怪的对象分配行为C