3

クリフノート:

あるケースではメソッドチェーンが期待どおりに動作するようになりましたが、別のケースでは何かおかしなことが起こっています。

これら 2 つの例の出力はまったく同じであると
予想
されます。


私は Javascript を使用して多くの連鎖を行ってきたので、C++ でも同様の方法でクラス メソッドを連鎖できることを知ったとき、試してみたいと思いました。しかし、いくつかの問題に遭遇しました。チェーンが問題を引き起こしているのか、それとも何か他のものなのかはわかりません。

への参照を返すことで連鎖を行っていますthis。例えば:

Class LLL
{
public:
    LLL & addNode( ... );

そして add Node メソッドは次のように終了します。

    ...
    return *this;
}

2 つの関連するクラスと 3 つの関連するメソッドがあります。

線形連結リスト クラスLLLとノードクラスがありNodeます。これをできるだけ単純にするために、ノードは単純にint(と呼ばれるguid) とnextandprevポインターを保持します。線形連結リストは、多数のノードを LLL に配置します。これはすべて正常に機能しますが、チェーンを使用してノードを追加すると、時々奇妙な結果が得られます。


期待どおりに動作する例:

    // Create 3 node objects & change their guids
Node node1; node1.change(1); // change(n) outputs "-n-"
Node node2; node2.change(2);
Node node3; node3.change(3);

    // Create a linear linked list object
LLL lll;
    // Add the nodes to the LLL and show the LLL
lll.addNode(node1).addNode(node2).addNode(node3).show();

// Expected and real output:
-1--2--3-123 

このコードパッドで試してみてください

ここで、ノード オブジェクトを 1 つだけ使用してみました。


何が起こっているのか理解できない例:

Node nod;
LLL lll;
lll.addNode(nod.change(1)).addNode(nod.change(2)).addNode(nod.change(3)).show();

// Expected output:
-1--2--3-123
// Output:
-3--2--1-111 

// Why are the changes being made in reverse order? And why do all three
//    nodes have a guid of 1?

このコードパッドで試してみてください

上記の 2 つの例は同じ結果になるはずです。

2 番目の例で単一ノード オブジェクトに何が起こるかについて、私は何かを誤解しているに違いないと思います。

上記のコードパッドのいずれかでコードを確認できます。以下の 2 番目の例の完全なコードを含めます。コードパッドに配置できるように、コードを 1 つの巨大なファイルに設定しましたがnode.hnode.cpp、 、lll.hlll.cpp、およびmain.cpp:

#include <cstdlib>
#include <iostream>
#include <cstring>
#include <ctime>
using namespace std;

// NODE.H ======================================================================*/
// This class holds the data
class Node
{
public:
    Node();
    Node(int guidIn);
    Node(const Node & nodeIn);
    Node & change(int guidIn);
    void commonConstructor();
    // Get the next node
    Node * next();
    // Set the next node
    void   next(Node * nodeIn);
    // Get the previous node
    Node * prev();
    // Set the previous node
    void   prev(Node * nodeIn);
    Node & show();
private:
    int guid;
    Node * nextNode;
    Node * prevNode;
};
/* EOF
   =============================================================================*/

// LLL.H =======================================================================*/
// This is a LLL to hold nodes
class LLL
{
public:
    LLL();
    ~LLL();
    LLL & addNode(const Node & nodeIn);
    LLL & show();
private:
    Node * head;
};
/* EOF
   =============================================================================*/

// NODE.CPP ====================================================================*/
Node::Node()
{
    guid = 0;
    commonConstructor();
}
Node::Node(int guidIn)
{
    guid = guidIn;
    commonConstructor();
}
Node::Node(const Node & nodeIn)
{
    guid = nodeIn.guid;
    commonConstructor();
}
Node & Node::change(int guidIn)
{
    guid = guidIn;
    cout << "-" << guidIn << "-";
    return *this;
}
void Node::commonConstructor()
{
    nextNode = NULL;
    prevNode = NULL;
}
Node * Node::next()
{
    return nextNode;
}
void Node::next(Node * nodeIn)
{
    nextNode = nodeIn;
}
Node * Node::prev()
{
    return prevNode;
}
void Node::prev(Node * nodeIn)
{
    prevNode = nodeIn;
}
Node & Node::show()
{
    cout << guid;
    return *this;
}
/* EOF
   =============================================================================*/

// LLL.CPP =====================================================================*/    
LLL::LLL()
{
    head = NULL;
}
LLL::~LLL()
{
    Node * temp = head;
    while(head)
    {
        temp = head;
        head = head->next();
        delete temp;
    }
}
LLL & LLL::addNode(const Node & nodeIn)
{
    Node * tempNode = new Node(nodeIn);
    if(!head)
    {
        head = tempNode;
    } else
    {
        Node * temp = head;
        while(temp->next())
        {
            temp = temp->next();
        }
        temp->next(tempNode);
    }
    return *this;
}
LLL & LLL::show()
{
    Node * temp = head;
    while(temp)
    {
        temp->show();
        temp = temp->next();
    }
    return *this;
}
/* EOF
   =============================================================================*/

// MAIN.CPP ====================================================================*/    
int main()
{
    Node node;
    LLL lll;
    lll.addNode(node.change(1)).addNode(node.change(2))
        .addNode(node.change(3)).show();

    cout << "\n";
    return 0;
}
/* EOF
   =============================================================================*/
4

3 に答える 3

4

2 番目のバージョンでは、同じノードで多くの操作を呼び出しており、評価順序の意味を考慮していません。式a.f(b)は と同じようなものでA::f(a, b)、ここaで は クラスAであり、コンパイラはパラメータaを自由に評価できb、適切と思われる順序で評価できます。

あなたの場合、コンパイラが式を見ると、を評価した後a.foo(b.bar(1)).foo(b.bar(2))に自由に評価し、 を呼び出すことができます。これにより、期待したものとは明らかに異なる動作が得られます。a.foo(b.bar(1)) b.bar(2)foo

つまり、同じ式でオブジェクトを複数回変更したり使用したりしないでください。私の哲学は、可能な限り、オブジェクトを変更しないことです。

于 2010-10-23T04:04:08.567 に答える
2

C++ は、部分式が評価される順序を保証しません。

これを取得する例では: -3--2--1-111 おそらく何が起こっているのですか?

lll.addNode(   // fourth
node.change(1) // third
)
.addNode(      // fifth
node.change(2) // second
)
.addNode(      // sixth
node.change(3) // this is first
)
.show();       // last

node に与えられた最後の値は 1 であり、addnode への 3 回の呼び出しすべてでそのノードへの参照を渡しているため、lll のすべてのノードは同じ値を取得します。

于 2010-10-23T04:03:35.700 に答える
0

技術的に言えば、シーケンス ポイント間で 1 つの変数 (ノード) を複数回変更しているため、これは未定義の動作です。また、ノードをconst参照ではなく値でリストに配置すると、リストから目的の出力が得られる場合があると思いますが、それでも未定義の動作です。

于 2010-10-23T13:06:42.840 に答える