【问题标题】:C++ - Best way to create a non-local variable pointerC++ - 创建非局部变量指针的最佳方法
【发布时间】:2020-12-30 13:01:22
【问题描述】:

我最近在使用 Unreal 引擎开发游戏时重新学习 C++。接触 C++ 已经快 3 年了,从那时起我主要使用 Java。

由于 java 和 c++ 之间的差异,我已经可以看出对于类似概念有不同的最佳实践。

我有两种这样的方法。

void UMarchingSquares::Generate(std::map<Vector2, int> automata) {
    std::map<Vector2,ControlNode*> controlNodes = getControlNodes(automata);
}

std::map<Vector2,ControlNode*> UMarchingSquares::getControlNodes(std::map<Vector2, int> automata) {
    std::map<Vector2,ControlNode*> controlNodes = std::map<Vector2, ControlNode*>();
    for(pair<Vector2,int> pair : automata) {
        Vector2 pos = pair.first;
        ControlNode node = ControlNode(pos,pair.second);
        controlNodes[pos] = &node;
    }
    return controlNodes;
}

我可能破坏了一些不同的 C++ 最佳实践,但有一个我真的想要澄清一个特定领域。

我在getControlNodes() 方法的for loop 中初始化ControlNode 对象。我现在知道这样做很糟糕,因为我存储了一个指向局部变量的指针,然后每次循环迭代都会超出范围。我更喜欢存储指针而不是实际的控制节点(尽管我可能会相信,否则,因为控制节点拥有一个位置 [2 个浮点数]、一个材质 [1 个整数],以及其他两个同时具有位置和材质的对象他们自己的。)

创建非局部变量指针的最佳方法是什么?我知道我可以只使用“new ControlNode()”,但据我所知,这最终是一个相当昂贵的调用,并且需要清理(这也可能很昂贵)。 我将相当频繁地调用这部分代码,所以我希望它高效。

谢谢!

【问题讨论】:

  • 为什么需要指针?
  • 如果您忘记了 Java,您的代码会变得更简单,以至于您的问题可能变得毫无意义(取决于您认为需要指针的原因)。 例如:std::map&lt;Vector2,ControlNode&gt; controlNodes; 不需要过多的初始化语法。
  • 假设您坚持使用指针,那么智能指针就是要走的路,std::unique_ptr&lt;ControlNode&gt;。您可能还想查看移动语义以提高效率。

标签: c++ pointers memory-management memory-leaks


【解决方案1】:

C++ 在过去几年中发生了很大变化,以使使用它的人的生活更轻松。

查看您的代码,它提出了很多问题:

  • 为什么您的映射值是指向 ControlNode 的原始指针,而不是按值显示的 ControlNode 或指向它的 unique_ptr
  • 在你的 for 循环中,为什​​么要写对的显式类型(与迭代器不同),auto 可以帮助你减少副本

由于您的问题是关于第一个,我将忽略第二个。

看到这里,你有 3 种修复代码的方法:

std::map<Vector2,ControlNode> getControlNodes(std::map<Vector2, int> automata) {
    auto controlNodes = std::map<Vector2, ControlNode>{};
    for(auto &&pair : automata) {
        auto &&pos = pair.first;
        auto node = ControlNode(pos,pair.second);
        controlNodes[pos] = std::move(node);
    }
    return controlNodes;
}

在这段代码中,您可以看到* 已从地图中删除。这意味着 ControlNode 的所有权被移动而不是地图。 (另请注意std::move)这类似于您将 int 存储在作为参数传入的映射中的方式。

如果您需要内存分配,因为您将移动它并且地址需要稳定,std::unique_ptr 是一个很好的解决方案。

std::map<Vector2,std::unique_ptr<ControlNode>> getControlNodes(std::map<Vector2, int> automata) {
    auto controlNodes = std::map<Vector2, std::unique_ptr<ControlNode>>{};
    for(auto &&pair : automata) {
        auto &&pos = pair.first;
        auto node = std::make_unique<ControlNode>(pos,pair.second);
        controlNodes[pos] = std::move(node);
    }
    return controlNodes;
}

如你所见,这段代码和前面很相似,我把map中的type替换了,把ControlNode的构造改成了std::make_unique。因此,您有一个 unique_ptr,其中包含对分配的内存的所有权(只要您拥有 unique_ptr,一切就保持有效)。

仅当您无法更改签名并且在 C++ 中被认为是不好的做法时才应使用第三种解决方案,因为它通过原始指针传递所有权。现在你的调用者负责显式清理内存,因为 C++ 没有垃圾收集。

std::map<Vector2,ControlNode*> getControlNodes(std::map<Vector2, int> automata) {
    auto controlNodes = std::map<Vector2, ControlNode*>{};
    for(auto &&pair : automata) {
        auto &&pos = pair.first;
        auto node = new ControlNode(pos,pair.second);
        controlNodes[pos] = node;
    }
    return controlNodes;
}

PS:我在代码中添加了一些 auto 以使 sn-ps 之间的更改最小化。

【讨论】:

  • 是的,你提到的前两个问题是因为我参加的旧 C++ 课程从未涉及唯一指针,以及他们只是希望我们知道有助于加快程序速度的“自动”语法.第二个选项似乎可能是最好的选择,因为传入的每个自动机都有大约 2500 个项目,而且这将被相当频繁地调用。谢谢!
【解决方案2】:

使用控制节点向量进行存储。每当您需要一个新的控制节点时,请在该向量上附加一个。不要使用指针,而是使用指向该向量的迭代器。确保您预先在该向量中预留了足够的插槽,否则您的迭代器将失效。

【讨论】:

    猜你喜欢
    • 2013-10-03
    • 1970-01-01
    • 2021-11-26
    • 2021-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-26
    相关资源
    最近更新 更多