我正在开发一个相对较大的库(约13000行)作为个人实用程序库。它仅使用STL容器和智能指针进行内存管理-但现在我发现自己处在一种情况下,由于STL显然缺乏明显的解决方案,因此我可以使用普通指针。不过,我想保持现代的C ++风格。
这是我的mcve:
结构Foo
-一个int
包装器结构,显示调用了哪些成员函数。
struct Foo {
Foo(int x) {
this->x = x;
std::cout << "constructor\n";
}
Foo(const Foo& other) {
this->x = other.x;
std::cout << "copy constructor\n";
}
~Foo() {
std::cout << "destructor\n";
}
int x;
};
看main
,我创建了一个struct Foo
的自动实例:
int main() {
Foo foo{ 0 };
std::cout << "\nfoo.x : " << foo.x << "\n\n";
}
输出>
constructor
foo.x : 0
destructor
那样容易。 -现在,如果我想指向foo
并操纵其内容。
std::unique_ptr
或std::shared_ptr
之类的智能指针不会达到这种效果(显然!)。使用std::make_unique<T>()
(/ std::make_shared<T>()
)将动态分配内存,并且仅复制值:
Foo foo{ 0 };
std::unique_ptr<Foo> ptr = std::make_unique<Foo>(foo);
ptr->x = 2;
std::cout << "\nptr->x : " << ptr->x << '\n';
std::cout << "foo.x : " << foo.x << "\n\n";
输出>
constructor
copy constructor
ptr->x : 2
foo.x : 0 // didn't change
destructor
destructor
所以这不起作用。但是,它们的void reset(pointer _Ptr = pointer()) noexcept
成员函数允许直接分配一个指针:
std::unique_ptr<Foo> ptr;
Foo foo{ 0 };
ptr.reset(&foo);
ptr->x = 2;
std::cout << "\nptr->x : " << ptr->x << '\n';
std::cout << "foo.x : " << foo.x << "\n\n";
输出>
constructor
ptr->x : 2
foo.x : 2
destructor
destructor //crash
但是崩溃! foo
正常解构,并且比std::shared_ptr
还想解构其已经解构的解引用值。
HEAP [main.exe]:为RtlValidateHeap(...)指定了无效的地址
使用const指针:
Foo foo{ 0 };
Foo* const ptr = &foo;
ptr->x = 2;
std::cout << "\nptr->x : " << ptr->x << '\n';
std::cout << "foo.x : " << foo.x << "\n\n";
输出>
constructor
ptr->x : 2
foo.x : 2
deconstructor
到目前为止,指针感觉是最好的选择。
我只是在寻找一种指向自动分配对象的方法。到目前为止,不返回简单的指针似乎很难。
我自己提供了一种解决方案-使用/*const*/ std::reference_wrapper<T>
。 (有关此解决方案,请随时纠正我)
但是我不确定什么是最佳策略。指针? std::reference_wrapper
也许吗?使用std::shared_ptr
/ std::unique_ptr
时是否出错?
是否有更好,更直接的STL类?
答案 0 :(得分:4)
由于您的目标尚不明确,我对此押注了一些。
如果您想以清晰的所有权和生命周期的方式强烈传递资源,那么智能指针是实现此目标的合适方法。标准库已经提供了它们,因此只需使用它们即可。
您已经发现它与自动存储持续时间的对象(您所说的“在堆栈上”)在很大程度上不兼容。您可以使用自定义的无操作删除器来解决它,但是老实说为什么呢?您只是让生活变得过于复杂。当您以这种方式创建对象时,将为您选择所有权和生存期,并且很容易出错(指针悬而未决!)。
只需让std::make_unique
或std::make_shared
为您动态分配Foo
,然后以通常的方式使用生成的智能指针。
但是,显然我们看不到您的设计,对此我们也不了解。
如果您要做的就是将对对象的引用传递给函数,这将做某事然后返回,然后简单地做到这一点!
void bar(const Foo& foo)
{
// do stuff
}
int main()
{
Foo foo;
bar(foo);
}
答案 1 :(得分:3)
但是我不确定什么是最佳策略。指针?
可能。如果需要指向一个对象,则指针是一个不错的选择。
参考可能是另一个:
Foo foo{ 0 };
Foo& ref = foo;
ref = 2;
如果引用足够,则通常比指针更可取。
使用
std::shared_ptr
/std::unique_ptr
时是否出错?
是的。您拥有自动变量的所有权。那是禁忌。
在第一个示例中,您制作了一个对象的副本,希望对副本进行修改以影响原始对象。
答案 2 :(得分:0)
Foo foo{ 0 };
std::reference_wrapper<Foo> ref = foo;
ref.get().x = 2;
std::cout << "\nref.x : " << ref.get().x << '\n';
std::cout << "foo.x : " << foo.x << "\n\n";
输出>
constructor
ref.x : 2
foo.x : 2
deconstructor
行为类似于指针。好吧,看看它的实现,它是一个指针:
template<class _Ty>
class reference_wrapper
: public _Weak_types<_Ty>::type
{
public:
/*...*/
private:
_Ty * _Ptr; //<---
};
对于pointer const
效果( Foo* const
),可以将其声明为const std::reference_wrapper<Foo>
-禁止更改其指针地址。
Foo foo{ 0 };
const std::reference_wrapper<Foo> ref = foo;
Foo foo2{ 2 };
ref = foo2; //error
错误C2678二进制'=':未找到采用'const std :: reference_wrapper'类型的左操作数的运算符(或没有可接受的转换)