【问题标题】:Correct way to claim ownership of existing raw pointer正确声明现有原始指针所有权的方法
【发布时间】:2015-09-25 17:09:00
【问题描述】:

我有一些代码声称拥有一系列原始指针的所有权,我想知道是否有一种可接受的方法来做到这一点?我正在寻找的是一种在更大程度上强制执行代码所有权的方法。主要是,我一直想知道我的构造函数是否应该直接采用唯一指针向量。

作为旁注,一旦获得所有权,数据就应该是不可变的。

代码大致遵循下面class X的模式。

#include <iostream>
#include <memory>
#include <vector>
using namespace std; // For readability purposes only

class X {
public:
    const vector< unique_ptr<const int> > data; // In my case this is private

    // Constructor: X object will take ownership of the data 
    // destroying it when going out of scope
    X (vector<int*> rawData) 
        : data { make_move_iterator(rawData.begin()), make_move_iterator(rawData.end()) }
    { }
};


int main() {
    // Illustrating some issues with claiming ownership of existing pointers:
    vector<int*> rawData { new int(9) , new int(4) };
    int* rawPointer = rawData[0];
    { // New scope
        X x(rawData);
        cout << *(x.data[0]) << endl; // Unique pointer points to 9
        *rawPointer = 7;
        cout << *(x.data[0]) << endl; // Unique pointer points to 7
    }
    cout << *rawPointer << endl; // The pointer has been deleted, prints garbage
    return 0;
}

【问题讨论】:

  • 单个拥有的原始指针已经够糟糕了。拥有原始指针的向量?就反模式而言,这很难被击败。
  • 请使用unique_ptr。
  • 这里不需要移动迭代器,因为int* 没有移动构造函数...
  • “我一直在想我的构造函数是否应该直接采用唯一指针向量”——是的,请。
  • 如果rawData的构造抛出分配它的缓冲区,初始化列表中的消息就会泄露。如果data 的构造在分配其缓冲区时抛出,则所有rawPointer 都会泄漏。您必须立即拥有那些已分配的指针以实现基本的异常安全。

标签: c++ pointers c++11 constants unique-ptr


【解决方案1】:

你犯了几个错误。

  1. 如果是const vector&lt; unique_ptr&lt;const int&gt; &gt; 数据;移动迭代器并没有多大意义。原因是,int* 没有移动构造函数。

  2. 如果你用vector &lt; int* &gt; 调用X 的构造函数X (vector&lt;int*&gt; rawData),那么vector &lt; int* &gt; 的复制构造函数就会被调用,但这不是你想要的。 顺便提一句。使用 move 的原因是为了避免大的内存副本。例如std::vector &lt; int* &gt;:std::vector&lt;int*&gt; 的成员属性大小和指向存储int*s 的内存位置的指针也必须通过移动复制,而不是int*s self。结论是,此举是为了获得所有权。

  3. 如果您想要这样的共享指针,请使用std::shared_ptr。它拥有一个计数器,用于计算指向自身的ptrs。

' 我的示例代码:

class X
{
public:
    const std::vector< std::shared_ptr< const int> > data; // In my case this is private

    // Constructor: X object will take ownership of the data 
    // destroying it when going out of scope
    X (std::vector<std::shared_ptr<int>>& rawData) 
        //: data(rawData)
        : data(rawData.cbegin(), rawData.cend())
    { }
};


int main() {
    // Illustrating some issues with claiming ownership of existing pointers:
    std::vector<std::shared_ptr<int>> rawData { std::make_shared<int>(9), std::make_shared<int>(4) };
    int* rawPointer = rawData[0].get();
    { // New scope
        X x(rawData);
        cout << *(x.data[0]) << endl; // Unique pointer points to 9
        *rawPointer = 7;
        cout << *(x.data[0]) << endl; // Unique pointer points to 7
    }
    cout << *rawPointer << endl; // The pointer has been deleted, prints not more garbage
    return 0;
}

如果你不想使用std::shared_ptr,你需要一个GC。

【讨论】:

  • 感谢您的回答,但不确定 shared_ptr 在这种情况下是否有意义,因为应该转让所有权,而实际上只有一个所有者。
  • 您想将所有权转让给 int* rawPointer?
  • 这个想法是 X 对象应该在构造时声明指针的所有权
【解决方案2】:

如果不详细了解您的情况,很难发布答案。但我的建议是在知道后立即将您的数据附加到unique_ptr。然后您可以随意将unique_ptr 移入和移出vectors。例如:

#include <iostream>
#include <memory>
#include <vector>
using namespace std; // For readability purposes only

class X {
public:
    const vector< unique_ptr<const int> > data; // In my case this is private

    // Constructor: X object will take ownership of the data 
    // destroying it when going out of scope
    X (vector<unique_ptr<const int>>&& v) 
        : data { std::move(v) }
    { }
};

vector<unique_ptr<const int>>
collectRawData()
{
    auto rawData = {9, 4};
    vector<unique_ptr<const int>> data;
    for (auto const& x : rawData)
        data.push_back(make_unique<int>(x));
    return data;
}

int main() {
    auto rawData = collectRawData();
    { // New scope
        X x(std::move(rawData));
        cout << *(x.data[0]) << endl; // Unique pointer points to 9
        cout << *(x.data[1]) << endl; // Unique pointer points to 4
    }
}

【讨论】:

  • 我认为你是对的,这似乎是要走的路,虽然移动和初始化唯一指针有点痛苦。
  • @Oystein 写 std::unique_ptr&lt;T&gt; to_unique(T* t){return std::unique_ptr&lt;T&gt;(t);} 和 make_unique&lt;T&gt;,以减少创建和/或窃取所有权的烦恼。 (永远,永远做一个to_unique( new T() ),做一个make_unique&lt;T&gt;())。移动它们是重点,如果您希望代码的工作不仅仅是“偶然”,那么记录所有权在代码中的位置很重要。
猜你喜欢
  • 1970-01-01
  • 2010-09-18
  • 2017-03-01
  • 2013-02-19
  • 1970-01-01
  • 2012-09-15
  • 1970-01-01
  • 2016-06-02
相关资源
最近更新 更多