1

我发现自己在争论是否要像 Code 1 和 Code 2 那样编写。在我看来,Code 1 看起来更干净,但从理论上讲,与 Code 2 相比,由于其额外的间接性,我是否可以期望性能损失?这里有任何相关的编译器优化吗?如果 bar() 返回 Bar*,有什么变化吗?

代码 1:

foo.bar().method1();
foo.bar().method2();
foo.bar().method3();
foo.bar().method4();

代码 2:

Bar& bar = foo.bar(); //Java programmers: ignore ampersand
bar.method1();
bar.method2();
bar.method3();
bar.method4();

编辑:我认为有太多变量要问这样一个一般性问题(例如,const 与非 const 方法,编译器是否内联方法,编译器如何处理引用等)。在汇编中分析我的特定代码可能是要走的路。

4

5 回答 5

1

与 Code_2 相比,Code_1 似乎有性能损失。

但请记住稳健 C++ 设计的最基本规则:-过早的优化是万恶之源。为了清楚起见,首先制作您的代码,然后指定一个好的分析器作为您的“大师”。

于 2014-12-31T19:47:23.357 回答
1

第二种选择Bar bar = foo.bar()肯定更有效,但多少取决于重量棒的重量。差异很可能是微不足道的。尝试基准测试。

至于可读性,我认为第二个选项更具可读性,但这属于个人风格。我认为您真正想要的是在method5内部调用所有四种方法。这样你就可以拥有

foo.bar().method5();

就是这样。

于 2014-12-31T19:49:08.303 回答
1

根据bar实际操作,可能会或可能不会(显着)性能损失。一个更有趣的问题是是什么让你认为你的第一种方法是“更干净”的。

在不了解实现的任何细节的情况下,我实际上倾向于相反的想法:后一种方法不仅更短(短是好的,因为更少的代码是更少的错误,更少的东西要阅读),而且更干净,更易读。

它清楚地反映了作者的意图,并且不会让读者对实现的细节感到疑惑bar,这可能会导致意想不到的副作用,而这反过来可能是也可能不是有意和/或期望的。

不要这样做,除非你有充分的理由这样做。

于 2014-12-31T20:03:57.033 回答
0

我认为在大多数情况下,这两种选择都不是一个好的选择。两者都通过破坏封装的引用公开内部结构。我认为 Foo 对象的抽象级别对于它的使用来说太低了,它应该提供更多类似服务的功能来使用它。

代替

Bar& bar = foo.bar(); //Java programmers: ignore ampersand
bar.method1(); 
bar.method2(); 
bar.method3(); 
bar.method4();

Foo 中应该有一些更高级别的方法

class Foo
{
public:
    void doSomething()
    {
        Bar& bar = foo.bar(); //Java programmers: ignore ampersand
        bar.method1(); 
        bar.method2(); 
        bar.method3(); 
        bar.method4();
    }

private:
    Bar& bar();
};

您永远不应该向客户提供对内部的非常量引用。

于 2015-01-01T15:42:51.210 回答
0

参考测试

我做了一个简单的测试。在没有优化的情况下编译时,在我的机器上 Test_1 花费了 1272 毫秒,而 Test_2 花费了 1108 次(我运行了几次测试,结果在几毫秒内)。 使用 O2/O3 优化,两个测试似乎都花费了相同的时间:946 毫秒。

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <chrono>

    using namespace std;

    class Foo
    {
    public:
      Foo() : x_(0) {}
      void add(unsigned amt)
      {
        x_ += amt;
      }
      unsigned x_;
    };

    class Bar
    {
    public:
      Foo& get()
      {
        return foo_;
      }
    private:
      Foo foo_;
    };

    int main()
    {
      srand(time(NULL));
      Bar bar;
      constexpr int N = 100000000;
      //Foo& foo = bar.get(); //TEST_2
      auto start_time = chrono::high_resolution_clock::now();
      for (int i = 0; i < N; ++i)
      {
        bar.get().add(rand()); //TEST_1
        //foo.add(rand()); //TEST_2
      }
      auto end_time = chrono::high_resolution_clock::now();

      cout << bar.get().x_ << endl;
      cout << "Time: ";
      cout << chrono::duration_cast<chrono::milliseconds>(end_time - start_time).count() << endl;
    }

指针测试

我重新运行了测试,但这次类成员是一个指针。在没有优化的情况下编译时,在我的机器上 Test_3 需要 1285-1340 毫秒,而 Test_4 需要 1110 毫秒。 使用 O2/O3 优化,两个测试似乎都花费了相同的时间:915 毫秒(令人惊讶的是,比上面的参考测试时间更短)。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <chrono>

using namespace std;

class Foo
{
public:
  Foo() : x_(0) {}
  void add(unsigned amt)
  {
    x_ += amt;
  }
  unsigned x_;
};

class Bar
{
public:
  ~Bar()
  {
    delete foo_;
  }
  Foo* get()
  {
    return foo_;
  }
private:
  Foo* foo_ = new Foo;
};

int main()
{
  srand(time(NULL));
  Bar bar;
  constexpr int N = 100000000;
  //Foo* foo = bar.get(); //TEST_4
  auto start_time = chrono::high_resolution_clock::now();
  for (int i = 0; i < N; ++i)
  {
    bar.get()->add(rand()); //TEST_3
    //foo->add(rand()); //TEST_4
  }
  auto end_time = chrono::high_resolution_clock::now();

  cout << bar.get()->x_ << endl;
  cout << "C++ Time: ";
  cout << chrono::duration_cast<chrono::milliseconds>(end_time - start_time).count() << endl;
}

结论

根据我机器上的这些简单测试,当启用优化时,Code 2 风格稍微快了约 15%,但启用优化后,性能没有差异。

于 2015-01-01T11:58:42.463 回答