【问题标题】:incremeting a value by cascading calls通过级联调用增加一个值
【发布时间】:2017-12-16 22:30:39
【问题描述】:

有人可以解释一下这段代码吗?我不明白 c2 的值怎么只有 1。

    class Test
{
private:
    static int c1;
    int c2 = 0;
public:
    Test fun();
    int getC1() { return c1; }
    int getC2() { return c2; }
};
int Test::c1 = 0;
Test Test::fun()
{
    c2 += ++Test::c1;
    return *this;
}
int main()
{
    Test t;
    Test t2 = t.fun().fun().fun().fun();
    cout << t.getC1() << " " << t.getC2();
    return 0;
}

谢谢。

【问题讨论】:

  • 我做到了,但问题是一切都发生在一行中(t2 = t.fun.fun...)
  • 哪个c2?您的程序创建了大约 6 个。
  • object t的c2。不应该是 10 吗?因为在 t 上调用 4 次 fun() 应该增加 c2 像:c2 += 1 , c2+= 2, c2+=3, c2+= 4. 这给我 c2 = 10。
  • 你只在t上打过一次fun()
  • 哦,是的,这是价值,谢谢

标签: c++ increment


【解决方案1】:

fun() 返回调用它的对象的副本。下一个调用是返回值。

也就是这段代码:

Test t2 = t.fun().fun().fun().fun();

等价于这段代码:

Test temp1 = t.fun();
Test temp2 = temp1.fun();
Test temp3 = temp2.fun();
Test t2 = temp3.fun();

fun() 递增调用它的实例的 c2 变量,然后返回一个副本。这意味着第一次调用增加t 的值,第二次增加temp1 的值,第三次增加temp2 的值,等等。创建的副本与源具有相同的c2 值。

如您所见,t.c2 只增加一次。

【讨论】:

    【解决方案2】:

    有人可以解释一下这段代码吗?我不明白怎么 c2的值只有1。

    我更喜欢 gdb,但我拒绝将匿名实例与(编译器提供的)默认 ctor、dtor 等结合使用。似乎没有地方可以设置断点或 cout 或介入...在总成层面上的工作太多。

    您可能考虑的另一种选择是添加一些诊断 cout 的 ... 以及复制和移动 ctors 以及带有诊断 cout 的 dtor。

    也许:


    #include <iostream>
    
    class Test
    {
    private:
       int        m_seq;
       int        c2;
       static int c1;
    
    public:
       Test()                    // default ctor
          : m_seq(++M_seq)
          , c2 (0)
          {
             std::cout << "\n  ctor  " << show() << std::flush;
          }
    
       Test(const Test& rhs)     // copy ctor
          : m_seq(++M_seq)       // unique seq id
          , c2 (rhs.c2)
          {
             std::cout << "\n  cctor " << show() << std::flush;
          }
    
       Test(const Test&& rhs)    // move ctor       (4)
          : m_seq(++M_seq)       // unique seq id
          , c2 (rhs.c2)
          {
             std::cout << "\n  mctor " << show() << std::flush;
          }  // tbd - new to me, should this be something with std::move() ?
    
       ~Test()                   // default dtor
          {
             std::cout << "\n  dtor  " << show() << std::flush;
          }
    
       Test fun();
    
       int getC1() { return c1; }
       int getC2() { return c2; }
    
       std::string show()
          {
             std::string s;
             s = " show()  seq: " + std::to_string(m_seq)
                + "    c1: " + std::to_string(c1)
                + "    c2: " + std::to_string(c2);
             return (s);
          }
    
    public:
       static int M_seq; // diagnostic only
    
    private:
       // coding standard - disallow when not used
       Test& operator= (const Test&)  = delete; // copy assignment (5)
       Test& operator= (const Test&&) = delete; // move assignment (6)
    
    };
    
    int Test::c1    = 0;
    int Test::M_seq = 0;
    
    Test Test::fun()
    {
       c2 += ++Test::c1;
    
       // diagnostic only
       std::cout << "\n  fun() " << show() << std::flush;
    
       return *this;
    }
    
    int main(int, char**)
    {
       // confirm initial seq state
       std::cout << "\n  -- init M_seq " << Test::M_seq << std::endl;
    
       Test t;
    
       Test t2 = t.fun().fun().fun().fun();
    
       std::cout << "\n\n   t final " << t.show() << std::endl; 
       std::cout <<  "\n  t2 final " << t2.show() << std::endl;
       // note how easy to ignore getters - 
       // see http://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html
    
       // final seq state
       std::cout << "\n\n  - final M_seq " << Test::M_seq << std::endl;
    }
    

    有输出:

      -- init M_seq 0
    
      ctor   show()  seq: 1    c1: 0    c2: 0
      fun()  show()  seq: 1    c1: 1    c2: 1
      cctor  show()  seq: 2    c1: 1    c2: 1
      fun()  show()  seq: 2    c1: 2    c2: 3
      cctor  show()  seq: 3    c1: 2    c2: 3
      fun()  show()  seq: 3    c1: 3    c2: 6
      cctor  show()  seq: 4    c1: 3    c2: 6
      fun()  show()  seq: 4    c1: 4    c2: 10
      cctor  show()  seq: 5    c1: 4    c2: 10
      dtor   show()  seq: 4    c1: 4    c2: 10
      dtor   show()  seq: 3    c1: 4    c2: 6
      dtor   show()  seq: 2    c1: 4    c2: 3
    
       t final  show()  seq: 1    c1: 4    c2: 1
    
      t2 final  show()  seq: 5    c1: 4    c2: 10
    
    
      - final M_seq 5
    
      dtor   show()  seq: 5    c1: 4    c2: 10
      dtor   show()  seq: 1    c1: 4    c2: 1
    

    c2的值只有1

    此输出列出了 ctor、fun() 和 cctor 调用。

    现在每个匿名对象都会打印其唯一的 m_seq,因此 seq id 应该有助于查看“操作”发生的位置。

    第一个 ctor 得到 seq 的 1 ...第一个 fun() 也是 seq 1 - 有意义。

    fun() 的后续调用在不同的实例中,最后 3 个是匿名的,这使得调试变得混乱。

    你是对的 - 第一个实例(带有 seq 1)以 c2 值 1 结束。以后 fun() 不会更改它。

    你也是正确的,最后一个实例(seq 5)以 c2 值 10 结束。

    在 4 个 fun() 链接在一起的序列中,最后 3 个创建 Test 的“临时”匿名实例,以及 2、3 和 4 个 dtor 与它们 ctor 在同一行。

    这种技术(使用诊断 couts 检测代码)很常见。在任何诊断 cout 前面加上一个 bool 以启用/禁用以进行更正式的测试……如果您不希望附带噪音,则在前面加上一个 bool 标识符以便于查找/删除。 (在生产代码中,我使用了基于 ram-based-round-robin 的日志。为了发布,此日志在系统启动时被抑制,并且没有日志会取消额外的 cout 活动。)

    您可以通过让 Test::fun() 返回指向实例的指针来真正改变这段代码的行为:Test*。该函数当前返回一个 Test 实例(即 *this),因此编译器生成一个 cctor 来构造该新的匿名实例。返回 this 指针会将 fun() 链更改为 fun()->fun()->fun()->fun()。

    希望这会有所帮助。

    【讨论】:

    • 感谢您的详细回复!现在很清楚了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-03
    • 1970-01-01
    • 2022-01-17
    • 2023-04-08
    • 1970-01-01
    • 2021-06-25
    • 1970-01-01
    相关资源
    最近更新 更多