尝试指向自动对象时应使用什么策略?

时间:2019-02-06 11:53:49

标签: c++ c++11 pointers memory-management reference

我正在开发一个相对较大的库(约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_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

但是崩溃! 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
  • 1个分配,1个释放。
  • 不复制。
  • 实际操作价值。

到目前为止,指针感觉是最好的选择。


我只是在寻找一种指向自动分配对象的方法。到目前为止,不返回简单的指针似乎很难。

我自己提供了一种解决方案-使用/*const*/ std::reference_wrapper<T>。 (有关此解决方案,请随时纠正我)

但是我不确定什么是最佳策略。指针? std::reference_wrapper也许吗?使用std::shared_ptr / std::unique_ptr时是否出错?

是否有更好,更直接的STL类?

3 个答案:

答案 0 :(得分:4)

由于您的目标尚不明确,我对此押注了一些。


如果您想以清晰的所有权和生命周期的方式强烈传递资源,那么智能指针是实现此目标的合适方法。标准库已经提供了它们,因此只需使用它们即可。

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

只需让std::make_uniquestd::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)

std::reference_wrapper

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'类型的左操作数的运算符(或没有可接受的转换)