C++ 这两个函数调用在内存管理方面的差异?

C++ 这两个函数调用在内存管理方面的差异?,c++,multithreading,memory-management,c++11,C++,Multithreading,Memory Management,C++11,假设ptr是指向T1类型的对象的指针,inst是T2类型的实例: T1* ptr(new T1); T2 inst; 我相应地设计了T1和T2的方法,这意味着在T1中,我几乎只有void函数可以操作这个对象,在T2中,我将有方法可以访问实际的成员。 所以我最后打了两个电话如下: ptr->doSomething(); inst.doSomething(); 考虑到这两个主要差异(指针与实例和实际调用->与)以及这个与成员值的使用,在多线程和高性能环境中,施加在ptr和inst上的内存模

假设
ptr
是指向
T1
类型的对象的指针,
inst
T2
类型的实例:

T1* ptr(new T1);
T2 inst;
我相应地设计了
T1
T2
的方法,这意味着在
T1
中,我几乎只有
void
函数可以操作
这个
对象,在
T2
中,我将有方法可以访问实际的成员。 所以我最后打了两个电话如下:

ptr->doSomething();
inst.doSomething();
考虑到这两个主要差异(指针与实例和实际调用
->
)以及
这个
成员值的使用,在多线程和高性能环境中,施加在
ptr
inst
上的内存模型是相同的吗?上下文切换、堆栈创建/分配、值访问等的成本如何

编辑:

奇怪的是,没有人提到分配者是一个新的玩家,可以改变游戏中的分配或位置


我想把重点放在内存模型上,放在硬件(主要是x86和ARM)内部的工作方式上。

这两个实例之间的主要区别与对象生命周期有关

T1
具有动态分配,这意味着其生命周期在调用
delete
时结束,
T2
具有自动分配,这意味着其生命周期在执行离开分配的封闭块时结束

在选择动态变量或自动变量时,对象生存期应该是主要的决策因素

第二个决定因素应该是对象大小。自动对象通常存储在大小有限的“堆栈”上。相反,动态分配的对象可以有更大的大小

第三个遥远的因素可能是引用的位置,这可能意味着,在某些场景中,间接(
->
)将造成一分钟的性能损失。这是只有分析人员才能知道的

我相应地设计了T1和T2的方法,这意味着在T1中 几乎只有void函数可以在这个 对象,在T2中,我将拥有访问实际 成员们

这没有多大意义。这两个类都可以有成员和非void函数

请注意,动态内存分配会带来成本,而且通常,内存分配器必须在内部获取锁。您可以尝试不同的分配器(如和其他分配器),它们在多线程场景中提供了一些性能改进

对于动态存储,还存在真正的内存泄漏线程,忘记调用
delete
。这可以通过使用智能指针来缓解,但它们会增加自己的性能损失

总的来说,在多线程环境中与否,唯一真正的问题是您是否真正需要动态分配提供的(生存期或大小)属性,是否愿意支付其性能成本


(你在做决定之前应该衡量的成本。完美是足够好的敌人。)

你的问题似乎很简单:调用“ptr->something()”和“instance.something()”有什么区别

从功能“某物”的角度来看,绝对没有

#include <iostream>

struct Foo {
    void Bar(int i) { std::cout << i << "\n"; }
};

int main() {
    Foo concrete;
    Foo* dynamic = new Foo;

    concrete.Bar(1);
    dynamic->Bar(2);

    delete dynamic;
}
我们的成员函数调用如下:

混凝土钢筋(1)

通常,所需的周期比

Foo* dynamic = new Foo;
dynamic->Bar(1);
delete dynamic;
因为第二种变体必须分配内存,而且通常内存分配器速度很慢(它们通常具有某种锁来管理共享内存池)。此外,为此分配的内存可能是缓存冷的(尽管大多数股票分配器会将块数据写入页面,导致在您开始使用它时,它会变得有点缓存热,但这可能会导致页面错误,或将其他内容从缓存中推出)

使用堆栈的另一个潜在优势是通用缓存一致性

int i, j, k;
Foo f1, f2, f3;
// ... thousands of operations populating those values
f1.DoCrazyMagic(f1, f2, f3, i, j, k);
如果
DoCrazyMagic
中没有外部引用,那么所有操作都将发生在一个小的内存区域中。相反,如果我们这样做

int *i, *j, *k;
Foo *f1, *f2, *f3;
// ... thousands of operations populating those values
f1->DoCrazyMagic(*f1, *f2, *f3, *i, *j, *k);
可以想象,在复杂场景中,变量将分布在多个页面上,并可能导致多个页面错误

然而,如果“数千个操作”足够密集和复杂,我们放置
i、j、k、f1、f2和f3的堆栈区域可能不再是“热的”


换句话说:如果滥用堆栈,它也会成为有争议的资源,并且相对于堆使用的优势会被边缘化或消除。

请注意,两者都在内部显式或隐式地使用
this
。注意:您的第二个“实例”不是;它是一个函数声明,用于一个名为
inst
的函数,该函数通过类型为
T2
的值返回一个对象,并且不带任何参数。我想你的意思是
t2inst我不确定这与多线程有什么关系,但是如果您想了解它生成的内容,请编写一些代码,并生成asm,看看它们是否相同,如果不相同,您可能会找到一些答案。或者你可以回来发布这个例子code@WhozCraig谢谢,我已经解决了这个问题。你的问题标题中的两项是不相关的,内存分配方法对函数的调用方式没有显著的影响,正如kfsone所演示的那样。+1只是为了添加-堆栈稍微快一些,堆更大,它们之间的选择是特定于用途的,不受限制scope@GlennTeitelbaum好的观点-有关于edit2的反馈吗?[注:我只看到了“和超出范围”,呵呵]当有人以简单易懂的方式明确提出问题时,请保存这篇文章并重新使用它。只有当指针值在多个线程之间共享时,才真正需要锁定内存,如果分配、使用和删除都发生在单个线程中,那么我就在这里
{
    Foo concrete;
    foo.Bar(1);
}
Foo* dynamic = new Foo;
dynamic->Bar(1);
delete dynamic;
int i, j, k;
Foo f1, f2, f3;
// ... thousands of operations populating those values
f1.DoCrazyMagic(f1, f2, f3, i, j, k);
int *i, *j, *k;
Foo *f1, *f2, *f3;
// ... thousands of operations populating those values
f1->DoCrazyMagic(*f1, *f2, *f3, *i, *j, *k);