我有一个我正在更新的Pre C ++ 11/14代码库。
代码几乎难以使用堆栈分配,然后传递这些指针,例如:
{
KlassX x;
KlassY y;
y.doThings( &x ); //Holds the pointer to KlassX for later use
return mainLoop(); //Doesn't return until the program finishes
}
在这种情况下,KlassY对* KlassX的内存分配一无所知。在这个特定的应用程序中它是安全的,但它不是“安全代码”,因为如果以不同的方式使用KlassY,它不会使用shared_ptr来跟踪其持有的指针的解除分配。
我宁愿实现KlassY来使用shared_ptr,而不必将程序更改为堆分配。有没有一种安全的方法可以继续使用堆栈分配方案,但让KlassY为KlassX使用shared_ptr?
如果这是重复,我道歉,我做了相当多的谷歌搜索,无法找到这个问题的答案。
答案 0 :(得分:12)
此处没有理由使用shared_ptr
。你没有分享所有权! doThings()
并不拥有&x
的所有权(它不可能这样做),它只是观察它。传递原始观察指针并没有错。如果你想在C ++ 11中强调这一点,你可以简单地定义:
template <class T>
using observer_ptr = T*;
(实际上它是library funtamentals TS v2中的一个真正的类类型,但基本上只是一个非常薄的原始指针包装器)并传递它。这在概念上或语义上与原始指针不同,我们会让所有各方更清楚无所有权在此处进行。只是观察语义。
此外,共享堆栈对象的所有权意味着什么?
答案 1 :(得分:-1)
几乎一切皆有可能
#include <memory>
template<typename T>
struct NonDeleter {
void operator()(T* t) {}
};
struct KlassX {};
class KlassY {
std::shared_ptr<KlassX> klassx_;
public:
KlassY() : klassx_(nullptr) {}
void doThings(std::shared_ptr<KlassX> klassX) {
klassx_ = klassX;
}
};
int mainLoop() { return 0; }
int main() {
KlassX x;
KlassY y;
std::shared_ptr<KlassX> xptr(&x, NonDeleter<KlassX>());
y.doThings(xptr);
return mainLoop();
}
但并非一切都是可取的。 shared_ptr
的目的是传达所有权,在这里我们正在歪曲这种意义。此外,在引擎盖下,shared_ptr可以在引擎盖下实现,以动态分配共享数据块。
当然,每次传递这个共享指针时,你也会给你的应用程序带来加重和减去引用计数的成本。
然而,这可能是一个轻微的理由来源:
KlassX x;
KlassY y;
std::shared_ptr<KlassX> xptr(&x, NonDeleter<KlassX>());
y.doThings(xptr);
int r = mainLoop();
// make sure we didn't leak references
assert(xptr.use_count() == 1);
return r;
我认为这种理由是非常脆弱的。
除非您计划成为引用计数检查程序并且希望将来支持共享动态分配处理,否则您应该坚持使用原始指针或利用提议的C ++ 17 observer ptr明确表明非所有权的概念。 [请参阅proposal了解可能的实施方案]
答案 2 :(得分:-1)
您可以设计KlassY::doThings
,使其接受std::shared_ptr
然后,您可以使用std::shared_ptr
的别名构造函数(有关详细信息,请参阅here)来解决您实际上不需要共享指针的事实您的示例代码。
这是我的意思的一个例子:
#include<memory>
#include<iostream>
void f(std::shared_ptr<int> ptr) {
std::cout << *ptr << std::endl;
}
int main() {
int x = 42;
std::shared_ptr<int> fake{};
std::shared_ptr<int> ptr{fake, &x};
f(ptr);
}
只要您确保x
的生命周期至少等于y
的生命周期,您就不会有任何问题。
请注意,这样您就不会为int
分配空间,并且您正在传递分配的堆栈,这就是您所要求的,并且如果需要,可以顺利迁移到新架构。