这个问题是为了确认我理解这个概念,并对使用方式和可能的优化方面采取专家意见。
我正在努力理解"放置新的"以下是我提出的计划......
#include <iostream>
#include <new>
class A {
int *_a;
public:
A(int v) {std::cout<<"A c'tor clalled\n";_a= new int(v);}
~A() {std::cout<<"A d'tor clalled\n"; delete(_a);}
void testFunction() {std::cout<<"I am a test function &_a = "<<_a<<" a = "<<*_a<<"\n";}
};
int main()
{
A *obj1 = new A(21);
std::cout<<"Object allocated at "<<obj1<<std::endl;
obj1->~A();
std::cout<<"Object allocated at "<<obj1<<std::endl;
obj1->testFunction();
A *obj2 = new(obj1) A(22);
obj1->testFunction();
obj2->testFunction();
delete(obj1);// Is it really needed now? Here it will delete both objects.. so this is not the right place.
//obj1->testFunction();
//obj2->testFunction();
return 0;
}
当我运行这个程序时,我得到了o / p
A c'tor clalled
Object allocated at 0x7f83eb404c30
A d'tor clalled
Object allocated at 0x7f83eb404c30
I am a test function &_a = 0x7f83eb404c40 a = 21
A c'tor clalled
I am a test function &_a = 0x7f83eb404c40 a = 22
I am a test function &_a = 0x7f83eb404c40 a = 22
A d'tor clalled
I am a test function &_a = 0x7f83eb404c40 a = 0
I am a test function &_a = 0x7f83eb404c40 a = 0
我有以下问题......
请指出你看到的任何我可以改进的东西,或者只是不要尝试。任何好的参考或阅读也是受欢迎的。
答案 0 :(得分:11)
这非常非常简单:malloc
可以被认为是做两件事:
不能保证实现实际使用auto obj1 = new std::string("1");
// ↑ can be thought of as equivalent to ↓
auto obj2 = (std::string*)malloc(sizeof(std::string));
new(obj2) std::string("2");
,但通常是这样。你无法假设它的实现,但为了理解它是一个好的假设。
因此,以下被认为是等同的:
delete
同样适用于delete obj1;
// ↑ can be thought of as equivalent to ↓
obj2->~std::string();
free(obj2);
:
new
当您看到delete
和new
的真实含义时,您可以轻松地推断出这一切:分配后跟构造函数调用,析构函数调用后跟释放。
当您使用展示位置#include <cstdlib>
#include <string>
#include <new>
using std::string;
int main() {
auto obj = (string*)malloc(sizeof(string)); // memory is allocated
new(obj) string("1"); // string("1") is constructed
obj->~string (); // string("1") is destructed
new(obj) string("2"); // string("2") is constructed
obj->~string (); // string("2") is destructed
free(obj); // memory is deallocated
}
时,您已决定单独处理第一步。内存必须以某种方式分配,你只需完全控制它的发生方式以及内存的来源。
因此,您必须分别跟踪两件事:
记忆的生命周期。
对象的生命周期。
下面的代码演示了这些代码是如何彼此独立的:
void ub() {
alignas(string) char buf[sizeof(string)]; // memory is allocated
new(buf) string("1"); // string("1") is constructed
} // memory is deallocated but string("1") outlives the memory!
如果对象的生命周期超过内存的生命周期,则程序具有UB。确保内存总是超过对象的生命周期。例如,这有UB:
void ub() {
alignas(string) char buf[sizeof(string)]; // memory is allocated
new(buf) string("1"); // string("1") is constructed
buf->~string(); // string("1") is destructed
} // memory is deallocated
但这没关系:
alignas
请注意您需要使用SQL Code Template正确对齐自动缓冲区。对于任意类型缺少struct S {
char str[10];
}
会导致UB。它似乎可行,但这只会误导你。
有些特定的类型没有调用析构函数而没有正确对齐内存并不会导致UB,但是你不应该假设有关类型的东西。调用你的析构函数并进行对齐,如果事实证明它是不必要的,它将不会花费你任何东西 - 不会为这种类型生成额外的代码。
{{1}}
答案 1 :(得分:5)
这可能适用于CodeReview.SE,让我在回答您的问题之前对您的源代码进行评论。
A *obj1 = new A(21);
std::cout<<"Object allocated at "<<obj1<<std::endl;
obj1->~A();
您通常不会在使用placement-new创建的而非对象上调用析构函数。在您的情况下,您将破坏旧的并使用placement-new构建一个新的。即使这样可行,您也应该实现一些重置功能来重置对象,而不是破坏和构建新对象。
17 obj1->testFunction();
这是UB。你已经破坏了对象,你不应该在它上面调用任何方法。
18 A *obj2 = new(obj1) A(22);
19 obj1->testFunction();
20 obj2->testFunction();
这是 okayish ,请注意,obj1
和obj2
是完全相同的对象。
21 delete(obj1);// Is it really needed now? Here it will delete both objects.. so this is not the right place.
你的评论错了。你没有删除两个对象,你要删除一个,稍后再删除。
22 obj1->testFunction();
23 obj2->testFunction();
这是 - 再次 - UB,不要在解构或删除的对象上调用方法。 对你的问题:
成员_a是动态分配的(没有新的展示位置)。那么为什么它为obj1&amp;获得相同的地址呢? obj2的。这只是巧合吗?
不要称它们为obj1
和obj2
,因为这两个变量指向同一个对象,但是,这是巧合。在第一个对象被破坏并释放了这个内存之后,第二个分配了相同数量的内存,它刚刚被释放,分配器决定给你完全相同的内存。
第15行的电话是一个好习惯吗?
不,不是。你需要调用析构函数的例子非常少,其中一个就是你的对象是由placement-new创建的。在你的例子中,这没有副作用,因为你在解构旧对象之后在同一个地方构造一个新对象,而新对象与旧对象的类型相同,否则这可能会以某种方式破坏。
现在更多关于删除后的评论。让我们看一下new
和一个新的实际位置。
新的做法:
this
)设置为分配器获取的内存块。删除恰恰相反:
现在到placement-new:placement-new只是跳过第一步(分配内存)并调用 new 对象的构造函数,并将this
设置为您传递的地址。因此,placement-new的相反只是调用析构函数,因为不存在放置删除。
这意味着对于你的代码,在你调用析构函数后,你的第一个对象死了但是你从未给过内存,这就是为什么你可以在那个内存中构造一个新对象的原因。现在当你调用delete时,第一个对象不再存在,只有它使用的内存,但是同一个内存现在被第二个对象阻挡,因此当你调用delete时你不删除两个对象,你只删除了第二个对象一个(你解构它然后释放大块的记忆)。
您可以在isocpp's faq
了解有关放置新主题以及何时调用析构函数的更多信息答案 2 :(得分:0)
C ++放置新工作原理如何?
...
我正在努力理解&#34;放置新的&#34;以下是我提出的计划......
这两个答案都很棒。但是你也想知道它是如何工作的,所以,我将从汇编中添加解释:
A *obj1 = new A(21);
:call operator new(unsigned long)
mov esi, 21
mov rdi, rax
mov rbx, rax
call A::A(int)
A *obj2 = new(obj1) A(22);
mov esi, 22
mov rdi, rbx
call A::A(int)
它是如何运作的,足够清晰,不再需要解释,对吗?