【问题标题】:C++ Container of Pointers/References to Existing Objects指向现有对象的指针/引用的 C++ 容器
【发布时间】:2022-01-22 18:35:36
【问题描述】:

我有一个现有的容器,里面装满了自动分配的结构对象,如下所示:

std::vector<Object> objectList {obj1, obj2, obj3};

我正在尝试创建一个新容器 useObjectList,其中包含指向这些现有对象的指针/引用,以便我能够访问和修改任一列表中的对象成员值。

我试图通过创建一个共享指针容器来实现这一点

std::vector<std::shared_ptr<Object>> useObjectList {std::make_shared<Object>(objectList[1])};

但它根本没有解决(鉴于我的知识有限,我假设 std::make_shared 完全构造和/或分配一个新对象?)。

如果我能得到任何建议,我将不胜感激。

【问题讨论】:

  • 它们是否意味着非拥有指针?如果是这样,std::vector&lt;Object *&gt; 可以解决问题(当然,您需要确保原始向量中的对象保持有效以避免悬空指针问题;特别是您可能不想修改原始向量任何可能将其大小调整为更大或更小的方式)
  • 使用原始指针std::vector&lt;Object*&gt; useObjectList {&amp;obj1, &amp;obj2, &amp;obj3};
  • make_shared 表示您的shared_ptr(s) 指向对象副本。如果您想要一个指向 same 对象的指针向量,请执行std::vector&lt;Object *&gt; useObjectList{&amp;obj1, &amp;obj2, &amp;obj3} 或(指向objectList 的元素,它们也是obj1 的副本.. obj3 不是引用/指向他们的指针)做std::vector&lt;Object *&gt; useObjectList{&amp;objectList[0], &amp;objjectList[1], &amp;objList[2]}
  • 在这种情况下可以使用原始指针吗?从人们告诉我的内容和我阅读的教程来看,他们总是建议避免使用原始指针。
  • 取决于你要对指针/引用/对象做什么。原始指针有其用途,就像“智能”指针一样。如果您(如您所愿)声明您需要一个指针/引用向量,但没有提及 为什么 您需要它,那么原始指针是一个不错的选择。

标签: c++


【解决方案1】:

要使用“现有对象的指针/引用的 C++ 容器”或其他任何东西,您应该了解所有权的概念。摘要:ownership is responsibility for cleanup。您应该考虑哪个实体应该删除您的对象。通常,每当您更改代码或向其中添加功能时,您都应该重新考虑这一点。

std::vector&lt;Object&gt; objectList {obj1, obj2, obj3}; - 这里,objectList 包含对象obj1obj2obj3 的副本,并负责删除它们(即拥有它们)。包含原始 obj1obj2obj3 的实体保留对它们的所有权(即将删除它们)。

更广泛的背景:

void do_stuff()
{
    Object obj1;
    Object obj2;
    Object obj3;
    std::vector<Object> objectList {obj1, obj2, obj3};
}

这里,函数do_stuff 的范围(或“堆栈框架”)拥有三个对象:当控制超出范围时,它们将被删除。这是一种对象以外的东西拥有对象的情况。

std::vector&lt;Object*&gt; objectList {&amp;obj1, &amp;obj2, &amp;obj3}; - 这里,objectList 包含指向对象obj1obj2obj3 的指针。对象本身的所有权并没有改变——拥有它们的东西会继续拥有它们。一些评论提到“总是建议避免使用原始指针”; “总是”是无稽之谈,但这是说明该建议的一个很好的例子。

更广泛的背景:

std::vector<Object*> someObjectList;
void do_stuff()
{
    Object obj1;
    Object obj2;
    Object obj3;
    std::vector<Object*> objectList {&obj1, &obj2, &obj3};
    someObjectList = objectList;
}

这里,函数do_stuff 的栈帧拥有这三个对象。如果您有指向这些对象的指针,则很容易将它们“发送”到其他实体 (someObjectList),即使在对象已被清理后也会存储这些指针。这就是人们“建议避免使用原始指针”的原因;但这里真正的问题更为根本——它有一个“指向现有对象的指针/引用容器”。这是人们不应该做的事情。

如果您真的想引用现有对象,则应确保您的所有权模式支持这一点。您可以通过以下任一方式执行此操作:

  1. 使用拥有对象的实体;确保在删除“所有内容的所有者”之前删除所有指向对象的指针列表。通常,您的应用程序的语义无论如何都需要这样做。

  2. 使用共享所有权。为此,您应该在对象的创建点应用共享所有权。

     std::shared_ptr<Object> obj1 = std::make_shared(whatever1);
     std::shared_ptr<Object> obj2 = std::make_shared(whatever2);
     std::shared_ptr<Object> obj3 = std::make_shared(whatever3);
     std::vector<std::shared_ptr<Object>> objectList(obj1, obj2, obj3);
    

    您应该考虑这样一种情况,即指向对象的唯一“链接”是您的指针列表。这在您的应用程序中有意义吗?如果是,请使用共享所有权。如果没有,这不是一个好主意,因为它隐藏了错误。

【讨论】:

    【解决方案2】:

    你可以使用reference_wrapper:

    list<int> l {1, 2, 3, 4, 5};
    vector<reference_wrapper<int>> v (l.begin(), l.end());
    
    // you can't use subscription in list
    // but as you are seeing elements by vector containing their references
    // so operator[] works here.
    
    cout << v[3] << '\n'; // outputs 4
    
    for (auto& num : l) {
        num *= 2; // original list elements are doubled
    }
    
    cout << v[2] << '\n'; // outputs 6
    
    
    

    【讨论】:

    • 这在什么方面优于原始指针?
    • 不需要在源代码中解引用,代码更简洁,没有缺点(在这种情况下),reference_wrappers 内部是指针
    猜你喜欢
    • 1970-01-01
    • 2014-01-02
    • 1970-01-01
    • 1970-01-01
    • 2015-02-13
    • 1970-01-01
    • 2013-10-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多