我发现自己在讨论是否要编写代码1和代码2之类的代码。在我看来,代码1看起来更清晰,但理论上,由于与代码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方法,编译器是否内联方法,编译器如何处理引用等)。在程序集中分析我的特定代码也许是要走的路。
答案 0 :(得分:1)
与Code_2相比,Code_1似乎有性能损失。
但请记住强大的C ++设计的最基本规则: - 过早优化是所有邪恶的根源。为了清晰起见,请先编写代码,然后指定一个好的分析器作为“Guru”。
答案 1 :(得分:1)
第二个选项Bar bar = foo.bar()
肯定更有效率,但多少取决于重量条的重量。这种差异很可能是微不足道的;尝试基准测试。
至于可读性,我认为第二种选择更具可读性,但这是个人风格。我认为你真正想要的是method5
,它在内部调用所有四种方法。因此你可以拥有
foo.bar().method5();
就是这样。
答案 2 :(得分:1)
取决于bar
实际执行的操作,可能会有(或明显的)性能损失。
一个更有趣的问题是,是什么让你认为你的第一种方法是更清洁"。
在不知道任何实现细节的情况下,我实际上倾向于认为相反:后者的方法不仅更短(短的是好的,因为更少的代码是更少的错误,更少的东西阅读),但也更清洁和更多可读。
它清楚地反映了作者的意图,并没有让读者想知道bar
的实施细节,这可能导致意料之外的副作用,而这反过来可能是也可能不是故意的和/或期望。
答案 3 :(得分:0)
参考测试
我跑了一个简单的测试。当使用 no optimizations 编译时,在我的机器上,Test_1耗时1272 ms,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;
}
指针测试
我重新尝试了测试,但这次是将类成员作为指针。使用 no optimizations 进行编译时,在我的机器上,Test_3需要1285-1340 ms,Test_4需要1110 ms。 使用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%左右,但启用了优化后,性能没有差异。
答案 4 :(得分: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();
};
你永远不应该向客户提供对内部的非const引用。