首页 > 解决方案 > 尝试指向自动对象时应该使用什么策略?

问题描述

我正在开发一个相对较大的库(~13000 行)作为个人实用程序库。它专门使用 STL 容器和智能指针进行内存管理——但现在我发现自己处于一种情况,由于在 STL 中明显缺乏明显的解决方案,我可以看到使用普通指针。不过,我想保持现代 C++ 风格。

这是我的mcve

Struct 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_ptrstd::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

但是崩溃!fooget 正常解构,并且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 类?

标签: c++c++11pointersmemory-managementreference

解决方案


对冲我的赌注,因为你的目标还不清楚。


如果您想以明确所有权和生命周期的方式强烈地传递资源,那么智能指针是执行此操作的合适方法。标准库已经提供了它们,所以只需使用它们。

您已经发现这在很大程度上与自动存储持续时间的对象(您所谓的“在堆栈上”)不兼容。您可以使用自定义的无操作删除器来解决它,但老实说为什么?你只是让生活对自己来说太复杂了。当您以这种方式创建对象时,将为您选择所有权和生命周期,并且容易出错(悬空指针,伙计们!)。

只需为您拥有std::make_uniquestd::make_shared动态分配您Foo的 s,并以通常的方式使用生成的智能指针。


但是,显然我们看不到您的设计,也不太了解它。

如果您需要做的就是将对对象的引用传递给函数,它会做一件事然后返回,那么只需这样做!

void bar(const Foo& foo)
{
    // do stuff
}

int main()
{
   Foo foo;
   bar(foo);
}

推荐阅读