3

悬崖笔记:

在一种情况下,我已经让方法链接按预期工作,但在另一种情况下,发生了一些有趣的事情。

我希望这两个示例具有完全相同的输出:
As expected example
Not as expected example


我已经使用 Javascript 进行了很多链接,所以当我了解到您可以在 C++ 中以类似的方式链接类方法时,我想尝试一下。我遇到了一些问题。我不确定是链接导致问题还是其他原因。

我通过返回对this. 例如:

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

然后添加节点方法以:

    ...
    return *this;
}

有 2 个相关的类和 3 个相关的方法。

我有一个线性链表类LLL和一个节点类,Node. 为了使这一点尽可能简单,节点只需保存一个int(称为guid)和一个nextprev指针。线性链表将一堆节点放在 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 

用这个键盘试试

现在我想只用一个节点对象来试试:


一个我不明白发生了什么的例子:

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?

用这个键盘试试

看起来上面的两个例子应该有相同的结果。

我想我一定是误解了第二个示例中单节点对象发生的事情。

您可以查看上面任一键盘中的代码,我将包含下面第二个示例的完整代码。我将代码设置为一个巨大的文件,这样我就可以将它放在键盘上,但我会评论什么是node.h, node.cpp, lll.h, lll.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

在第二个版本中,您在同一个节点上调用许多操作,而不考虑评估顺序的含义。该表达式a.f(b)A::f(a, b), where ais of class类似,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

由于给节点的最后一个值是 1,并且您在对 addnode 的所有三个调用中都传递了对该节点的引用,因此 lll 的所有节点都获得相同的值。

于 2010-10-23T04:03:35.700 回答
0

从技术上讲,这是未定义的行为,因为您在序列点之间多次更改一个变量(节点)。我还猜想,如果您按值而不是通过 const 引用将节点放入列表,那么您可能会从列表中获得所需的输出,但它仍然是未定义的行为。

于 2010-10-23T13:06:42.840 回答