由于复杂的情况(在前面的问题Constructing an object to return by value elsewhere中解释),我希望通过函数X中的值返回一个对象,但是在由X间接调用的另一个函数Y中创建它。在它们之间有第三方代码调用堆栈,在传递对象时不会合作。 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
变量的指针调用其他内容,最终{{1}在其他地方调用,接收许多具有复杂生命周期的参数。
wrapper.init
中创建的对象是否安全地传递到WrapA::init
并在main
中妥善处理?调用WrapA::~WrapA
时一切正常吗?代码有问题吗?
答案 0 :(得分:1)
您可以查看管理像wrapA这样的资源的类,您需要基本上提出两个问题:
让我们从1开始。我看到了一些潜在的问题:
至于2:
简而言之,wrapA并非完全错误,因为您可以完美地使用它(如您所示)。但是,它也不完全正确。它不能满足您期望c ++类满足的保证,因此我认为使用wrapA编写错误代码会很容易。我认为如果你修复了有关析构函数和复制构造函数/赋值的问题,那么使用起来会更安全。