gpt4 book ai didi

c++ - 放置新的,按值返回并安全地处理临时拷贝

转载 作者:行者123 更新时间:2023-11-30 05:41:37 24 4
gpt4 key购买 nike

由于情况复杂(在前面的问题 Constructing an object to return by value elsewhere 中解释过)我想从函数 X 按值返回一个对象,但在 X 间接调用的另一个函数 Y 中创建它。在它们之间有调用中的第 3 方代码不会合作传递对象的堆栈。 X 只能将指针传递给 Y 并接收返回的指针。

我想出了一个使用 placement new 的解决方案,但主要担心它是否可移植、不会调用任何未定义的行为并安全地处理分配的对象。也欢迎任何避免不必要的拷贝的改进。这是一个尽可能精简的完整测试程序:

#include <new>
#include <type_traits>
#include <cstdio>

class A {
public:
A() {
printf("Create A @ %p\n", this);
}

A(const A &other) {
printf("Copy A @ %p\n", this);
printf("From another A %s @ %p\n", other.valid ? "OK" : "NOT OK", &other);
valid = other.valid;
}

A(A &&other) {
printf("Move A @ %p\n", this);
printf("From another A %s @ %p\n", other.valid ? "OK" : "NOT OK", &other);
valid = other.valid;
}

~A() {
printf("Destroy A %s @ %p\n", valid ? "OK" : "NOT OK", this);
valid = false;
}

void bar() {printf("Hello, World! (A %s @ %p)\n", valid ? "OK" : "NOT OK", this);}

bool valid = true;
};

class WrapA {
public:
WrapA() {printf("Create wrapper! (A @ %p)\n", &data);}

~WrapA() {
printf("Destroy wrapper! (A %s @ %p)\n", reinterpret_cast<A *>(&data)->valid ? "OK" : "NOT OK", &data);
// Manually call destructor for instance created using placement new
reinterpret_cast<A *>(&data)->~A();
}

void init() {
::new(&data) A();
}

A getA() {
printf("Wrapper returning A %s @ %p\n", reinterpret_cast<A *>(&data)->valid ? "OK" : "NOT OK", &data);

return(*reinterpret_cast<A *>(&data));
}

typename std::aligned_storage<sizeof(A), alignof(A)>::type data;
};

A debug(A data) {
printf("Wrapper returned A %s @ %p\n", data.valid ? "OK" : "NOT OK", &data);
return(data);
}

A test() {
WrapA wrapper;

wrapper.init();

return(debug(wrapper.getA()));
}

int main(void) {
test().bar();

return(0);
}

它打印:

Create wrapper! (A @ 0x7fff1d6a5bde)
Create A @ 0x7fff1d6a5bde
Wrapper returning A OK @ 0x7fff1d6a5bde
Copy A @ 0x7fff1d6a5bdf
From another A OK @ 0x7fff1d6a5bde
Wrapper returned A OK @ 0x7fff1d6a5bdf
Move A @ 0x7fff1d6a5c0f
From another A OK @ 0x7fff1d6a5bdf
Destroy A OK @ 0x7fff1d6a5bdf
Destroy wrapper! (A OK @ 0x7fff1d6a5bde)
Destroy A OK @ 0x7fff1d6a5bde
Hello, World! (A OK @ 0x7fff1d6a5c0f)
Destroy A OK @ 0x7fff1d6a5c0f

输出显示 A 通过了 3 个不同的内存地址,在整个过程中保持有效并且所有拷贝似乎都被正确销毁了。在示例中,test 直接调用了 init,但在实际情况中,test 使用指向 的指针调用了其他东西wrapper 变量,最终 wrapper.init 在别处被调用,接收大量具有复杂生命周期的参数。

WrapA::init 中创建的对象是否安全地传递到 main 并在 WrapA::~WrapA 中适本地处理? A::bar() 被调用时一切正常吗?代码有问题吗?

最佳答案

您可以查看一个管理资源的类,例如 wrapA,您基本上需要问两个问题:

  1. 它是否正确管理其资源:正确构建、分配、销毁。
  2. 它的任何公共(public)数据或功能是否有可能轻易破坏资源管理方案?

让我们从 1 开始。我发现了一些潜在的问题:

  • 该类有一个数据成员,它表示容纳 A 的空间,但不一定是实际的 A。这很好
  • 但是,wrapA 的构造函数不会构造 A,但析构函数会尝试破坏 A。因此,如果您忘记对 wrapA 调用 init,就会出现未定义的行为。我会改变这个设计;最基本的方法是使用一个 bool 标志来跟踪 A 是否已实际构造。
  • 但是,wrapA 将获得自动构造的复制构造函数/赋值(已弃用)。这些自动生成的函数不会正确调用 A 的复制构造函数/赋值,因为 wrapA 实际上并不拥有 A,它们只会按位复制 A。所以如果 A 是非平凡的,这些函数将无法正常工作。您应该明确地编写这两个函数,或者 = 删除它们,以便 wrapA 变得不可复制。虽然 wrapA 将既不可复制又不可移动,因此使用起来可能很烦

至于 2:

  • getA 函数很好,因为它返回一个拷贝,所以不提供内部资源的句柄

简而言之,wrapA 并非完全错误,因为您可以很好地使用它(如您所演示的)。然而,这也不完全正确。它不满足您期望 c++ 类满足的保证,因此我认为使用 wrapA 很容易编写错误代码。我认为如果您解决有关析构函数和复制构造函数/赋值的问题,使用起来会安全得多。

关于c++ - 放置新的,按值返回并安全地处理临时拷贝,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31091223/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com