3

データ ファイルから多くのオブジェクトを読み込んでメモリに保存する必要があるプロジェクトに取り組んでいます。スタックスペースはまれであり、大量のデータをヒープに配置する必要があると言われたので、すべてをヒープに配置しました。ただ、ちょっとやり過ぎた印象です。

私の現在のデザインは次のようになります。

class RoadMap
{
    unique_ptr<set<unique_ptr<Node>>> allNodes;

    void addNode(unique_ptr<Node> node)
    {
        this->allNodes->insert(std::move(node));
    }
}

int main()
{
    unique_ptr<RoadMap> map(new RoadMap());

    // open file etc.

    for (auto nodeData : nodesInFile)
    {
        map->addNode(unique_ptr<Node>(new Node(nodeData)));
    }
}

私が今理解していることから、これは多くのオーバーヘッドを生み出します。これは、私が必要ではないと思う多くの一意のポインターが関係しているためです。私の理解が正しければ、「ポインター チェーン」に一意のポインター バリアが 1 つあれば十分です。ただし、これを行うためのベストプラクティスが何であるかはわかりません。

オプション1

class RoadMap
{
    unique_ptr<set<Node>> allNodes;

    void addNode (Node node)
    {
        this->allNodes->insert(node);
    }
}

int main()
{
    RoadMap map;
    //open file etc.
    for (auto nodeData : nodesInFile)
    {
        map.addNode(Node(nodeData));
    }
}

これの利点は、RoadMapクラス自体がヒープ割り当てを処理する必要がある唯一のものであり、set.

オプション 2

class RoadMap
{
    set<Node> allNodes;

    void addNode (Node node)
    {
        this->allNodes.insert(node);
    }
}

int main()
{
    unique_ptr<RoadMap> map(new RoadMap());
    // open file etc.
    for (auto nodeData : nodesInFile)
    {
        map->addNode(Node(nodeData));
    }
}

ここでは、一意のポインターはメイン関数内にのみあります。つまり、RoadMapクラスのユーザーは、このオブジェクトが非常に大きくなる可能性があり、スタックに置く必要があることを知る必要があります。これはあまり良い解決策ではないと思います。

オプション 3

class RoadMap
{
    set<unique_ptr<Node>> allNodes;

    void addNode(unique_ptr<Node> node)
    {
        this->allNodes.insert(std::move(node));
    {
}

int main()
{
    RoadMap map;
    // open file etc.
    for (auto nodeData : nodesInFile)
    {
        map.addNode(unique_ptr<Node>(new Node(nodeData)));
    }
}

このソリューションは多くの一意のポインタを使用します。つまり、RoadMap多くのデストラクタを削除するときにdeletes を呼び出す必要があります。また、RoadMap呼び出し元はunique_ptr、ノードを追加するときに を指定する必要があります。つまり、ヒープ割り当てを自分で行う必要があります。


現在、私は他のオプションよりもオプション 1 を支持しています。しかし、私は C++ のコーディングを比較的短期間しか行っておらず、メモリ管理の背後にある概念を完全に理解しているかどうか確信が持てません。オプション1がこれを行うための最良の方法であると仮定するのは正しいですか? この種のベスト プラクティスへの追加の参照はありますか?

4

4 に答える 4

5

ムーブNodeコンストラクターとムーブ代入演算子を指定して (セットに対する操作を安価にするため)、オプション 1 と 2std::setを組み合わせて使用RoadMap​​します。s をセットに移動できるようにするための余分なstd::move内部に注意してください。addNodeNode

class RoadMap
{
    set<Node> allNodes;

    void addNode (Node node)
    {
        allNodes.emplace(std::move(node));
    }
};

int main()
{
    RoadMap map;
    // open file etc.
    for (const auto& nodeData : nodesInFile)
    {
        map.addNode(Node(nodeData));
    }
}
于 2013-08-20T07:45:09.257 に答える
1

メタの問題について少し話させてください。スタックがオーバーフローしてデータ構造がヒープに置かれるのは望ましくありません。それは正しいことです。しかし、ここで理解しておくべき重要なことは、物事がいつヒープに置かれるかということです。

すべてのローカル変数はスタックに割り当てられます。動的サイズのデータ​​構造がある場合、それらは (ほぼ) すべてのケースでヒープを参照します。(私が知っている唯一の例外は、alloca()またはstd::get_temporary_buffer()そのようなもので意図的にスタックにメモリを予約する場合です)。特に、すべての STL コンテナーはメモリをヒープに保持し、ローカル変数またはメンバー変数用のスタック メモリはほとんど使用されません (std::arrayサイズがコンパイル時にわかっている場合を除く)。

したがって、スタックメモリを節約したい場合、動的にサイズ変更されたデータ構造をラップしunique_ptrsてもほとんど効果はありませんが、プログラムに間接性が追加され、コードが複雑になり、実行が遅くなり、ヒープメモリの使用量が不必要に増加します。

例を次に示します。32 ビット コンパイルstd::setを使用する Visual Studio 2010 では、テンプレート タイプ パラメータとセットに含まれる実際の要素数に関係なく、スタック上の 20 バイトのメモリが使用されます。セット要素のメモリはヒープ上にあります。

unique_ptrs意図した目的で使用するかどうかは、ご自身でご判断ください。

于 2013-08-20T13:59:04.490 に答える
1

それらのそれぞれは、互いにかなり異なっています。

簡単にするために、オプション 2 をお勧めします。ただし、ポインターではなくsort全体を移動するため、などの一部の操作ではパフォーマンスが高くなる可能性があります。Node

を使っているので問題ないと思いますset。オブジェクトで移動セマンティクスを使用することで、これを最適化できNodeます。これがないと、追加ごとに 1 つのコピーを使用することになります。

私が言及した上記の問題は、 の問題であった可能性がありますvector。オブジェクトを直接保存する場合に発生するもう 1 つの問題は、ポリモーフィズムの欠如です。のサブタイプを保存するNodeことはできません。それらはスライスされます。

これが問題である場合は、オプション 2 をお勧めします。ポインターを保存すると、ポインターの移動が速くなり、ポリモーフィズムが機能します。

オプション1または元のソリューションの理由はわかりません。

psthis->は不要です。

pps DyP が指摘するようsetに、とにかくヒープを使用します。これがオプション 2 を優れたものにしています。手がかり - スタックベースの構造は成長できません。=>std::arrayスタックに保存されていると思います。

于 2013-08-20T07:29:02.643 に答える