链接方法给了我意想不到的结果,并且参数正在以相反的顺序求值

发布于 2024-09-28 20:22:46 字数 5222 浏览 1 评论 0原文

Cliffnotes:

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

我希望这两个示例具有完全相同的输出:
如预期示例
不符合预期的示例


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

我通过返回对 this 的引用来进行链接。例如:

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

然后 add Node 方法以:

    ...
    return *this;
}

There's 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?

用这个Codepad尝试一下

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

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

您可以查看上面任一键盘中的代码,我将包含下面第二个示例的完整代码。我将代码设置为一个大文件,以便我可以将其放在键盘上,但我将在 node.hnode.cpp 中进行注释、lll.hlll.cppmain.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
   =============================================================================*/

Cliffnotes:

I've gotten method chaining to work as I expected in one case, but in another case, there is something funny going on.

I expect these two example to have the exact same output:
As expected example
Not as expected example


I've done a lot of chaining using Javascript, so when I learned that you can chain class methods in a similar way in C++, I wanted to give it a try. I ran into some problems though. I'm not sure if it's the chaining causing the problems, or something else.

I'm doing the chaining by returning references to this. For example:

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

and then the add Node method ends with:

    ...
    return *this;
}

There's 2 pertinent classes and 3 pertinent methods.

I have a linear linked list class, LLL and a Node classs, Node. To keep this as simple as possible a node simply holds an int (called guid) and a next and prev pointer. A linear linked list puts a bunch of nodes in a LLL. This all works fine, but when I add nodes using chaining I sometimes get odd results.

An example where things work like I expect:

    // 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 

Try it out with this Codepad

Now I wanted to try it with just one node object:

An example where I don't understand what is going on:

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?

Try it out with this Codepad

It seems like the two examples above should have identical results.

I think I must be misunderstanding something about what happens to the single node object in the second example.

You can look at the code in either of the codepads above, and I'll include the complete code for the second example below. I set up the code to be one huge file, so that I can put it on codepad, but I'll comment in what would be node.h, node.cpp, lll.h, lll.cpp, and 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
   =============================================================================*/

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

七七 2024-10-05 20:22:46

在第二个版本中,您在同一节点上调用许多操作,并且不考虑计算顺序的含义。表达式 af(b)A::f(a, b) 类似,其中 a 属于类 A,编译器可以按照它认为合适的任何顺序自由评估参数 ab

在您的情况下,当编译器看到表达式 a.foo(b.bar(1)).foo(b.bar(2)) 时,可以自由地评估 a .foo(b.bar(1)) 之后它评估b.bar(2),然后调用foo,这显然会产生与您期望的不同的行为。

简而言之,永远不要在同一个表达式中多次改变和使用一个对象。我的哲学是,如果可能的话,永远不要改变物体。

In the second version, you are invoking many operations on the same node, and not considering the implications of evaluation-order. The expression a.f(b) is kind of the same as A::f(a, b), where a is of class A, and the compiler is free to evaluate the parameters a and b in any order it sees fit.

In your case, when the compiler sees the the expression, a.foo(b.bar(1)).foo(b.bar(2)), it is free to evaluate a.foo(b.bar(1)) after it evaluates b.bar(2), and then call foo, which obviously yields different behaviour from what you expect.

In short, never mutate and use an object multiple times in the same expression. My philosophy is, if at all possible, never mutate objects.

潇烟暮雨 2024-10-05 20:22:46

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 的所有节点都会获得相同的值。

C++ does not guarantee what order sub-expressions will be evaluated in.

In the example where you get this: -3--2--1-111
What's happening probably, is this:

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

Since the last value given to node was 1, and you are passing a reference to that node in all to three calls to addnode, all nodes of lll get the same value.

黯然 2024-10-05 20:22:46

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

Technically speaking this is undefined behaviour as you are changing one variable (node) multiple times between sequence points. I also guess that you MAY get desired output from the list if you put node to the list by value, not by const reference but it still is undefined behaviour.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文