读者若是以为我文章还不错的,但愿能够多多支持下我,文章能够转发,可是必须保留原出处和原做者署名。更多内容请关注个人微信公众号:cpp手艺人linux
//oa的一系列操做...
OptimizationA GetOpt() {
OptimizationA oa;
//oa的一系列操做...
return oa;
}
void GetOpt(OptimizationA &_result) {
// result的一系列操做...
return;
}
复制代码
思考:效率是同样的?若是是不同的,那么又是如何不同的?那咱们如何作效率更好呢?c++
咱们本身写的代码,本身看一回事,可是在编译器的角度来看又是一番风景。因此此次咱们换个角度来看待问题,分别从初始化操做、优化、成员列表初始化三个方面探究下编译器会怎么翻译咱们的代码。程序员
OptimizationA oe;
OptimizationA of(oe);
OptimizationA og = oe;
OptimizationA oh = OptimizationA(oe);
// 编译器的角度看,分红两步走,
// 第一步:定义变量(不会调用初始化操做),第二步:调用拷贝构造
// 1.OptimizationA of (注意此时不会调用OptimizationA的默认构造函数)
// 2.of.OptimizationA::OptimizationA(oe) (调用拷贝构造函数)
// 3.og.OptimizationA::OptimizationA(oe) (调用拷贝构造函数)
// 4.oh.OptimizationA::OptimizationA(oe) (调用拷贝构造函数)
复制代码
void Parameter(OptimizationA oa) {
}
{
OptimizationA tempoa;
Parameter(tempoa);
}
// 编译器生成的代码
OptimizationA _tempObj<font>;
// tempObj调用copy构造
tempObj.OptimizationA::OptimizationA(tempoa);
Parameter(tempObj);
// tempObj调用析构函数,销毁对象
tempObj.OptimizationA::~OptimizationA();
复制代码
OptimizationA GetOpt() {
OptimizationA oa;
return oa;
}
// 此为编译器的生成的函数,分为两步操做
// 第一步:将上面的函数重写为下面的带引用参数的形式
void GetOpt(OptimizationA &_result) {
OptimizationA oa;
//oa的一系列操做。。。。。。
// 第二步:在return返回以前,调用result的copy 构造函数
result::OptimizationA::OptimizationA(oa);
return;
}
// 下面是编译器生成的调用代码
// 1.形式转换成这样
OptimizationA result;
GetOpt(result);
// 2.若是用户调用了类成员函数
GetOpt().GetHello();
// 编译器则转换成这样
(GetOpt(result), result).GetHello();
// 3.若是是用户定义了函数指针
OptimizationA (*pf)();
pf = GetOpt; // 没有参数
// 编译器则转换成这样
void (*pf)(OptimizationA &);
(pf(result), result).GetHello();
复制代码
// 程序员的未优化
OptimizationA GetOpt(const T &y, const T &x) {
OptimizationA oa(x, y);
// oa其余操做
return oa;
}
// 在linux上测试须要关闭优化选项
// 先是生成了一个临时对象tempobj,而后调用tempobj的拷贝构造函数,将oa的数据拷贝到
// tempobj中,而后在调用oa的析构函数。
// 这个过程当中消耗了一个tempobj的拷贝构造和析构函数
// 程序员优化,这样作就少了一个临时对象的生成和销毁
OptimizationA GetOpt(const T &x, const T &y) {
return OptimizationA(x, y);
}
复制代码
未优化代码 | 优化代码 |
---|---|
Linux上关闭优化选项结果: compiler:1 level:2 call ctor compiler:2 level:3 call copy ctor compiler:1 level:2 call dtor compiler:3 level:4 call copy ctor compiler:2 level:3 call dtor compiler:3 level:4 call dtor Linux不关闭优化选项: compiler:1 level:2 call ctor compiler:1 level:2 call dtor windows上: compiler:1 level:2 call ctor compiler:2 level:3 call copy ctor compiler:1 level:2 call dtor compiler:2 level:3 call dtor |
Linux: compiler:1 level:2 call ctor compiler:1 level:2 call dtor 在windows上: compiler:1 level:2 call ctor compiler:1 level:2 call dtor |
// 程序员写的代码
OptimizationA GetOpt() {
OptimizationA oa;
return oa;
}
// 编译器生成的代码:(named return value (NRV))
// 分为两步操做
// 第一步:将上面的函数重写为下面的带引用参数的形式
void GetOpt(OptimizationA &_result) {
OptimizationA oa;
//oa的一系列操做...
// 第二步:在return返回以前,调用__result的copy 构造函数
__result::OptimizationA::OptimizationA(oa);
return;
}
复制代码
先来看段代码:windows
class InitialzationB {
public:
// InitialzationB()
// {}
InitialzationB(int value): m_IA(value), m_a(value), m_b(value)
/* 放在初始化列中…… 1.若是是在成员列表初始化,站在编译器的角度看 m_IA.InitialzationA::InitialzationA(value) */
{
/* 放在构造函数中….. m_IA = value; 2.若是是在函数内部初始化,站在编译器的角度看 A.先是生成一个临时对象 InitialzationA oc; oc.InitialzationA::InitialzationA(value); B.在m_IA的copy ctor m_IA.InitialzationA::InitialzationA(oc); C.临时对象再去销毁 oc.InitialzationA::~InitialzationA(); 因此成员变量初始化会提升效率,但只针对类类型变量,对基本类型无影响。 在初始化列表中,不要用类成员变量去初始化另一个成员变量 */
}
private:
InitialzationA m_IA; // 自定义class
int m_a;
int m_b;
};
复制代码
InitialzationB(int value): m_IA(value), m_a(value), m_b(value) 这就是初始化列表的调用方法微信
简单来讲为了初始化对象时的效率。看上面的代码第7行放在初始化列中,从编译器的角度看就是直接调用了InitialzationA的构造函数。可是你若是放在16行,那么在编译器的角度看就是先生成了一个InitialzationA临时对象,在调用m_IA的copy构造函数,而后临时对象的消亡调用析构函数。因此大费周章的构造对象形成效率的降低。 调用时机:编译器会在构造函数以前会插入一段额外的代码,这就是initialization list。而后在执行用户写的代码。函数
1. 成员变量是个引用 |
2. 成员变量是const类型 |
3. 成员变量是带参数的构造函数类类型 |
4. 基类有带参数的构造函数 |
初始化顺序是按照在类中的声明顺序的来决定。因此在类的初始化列表中仍是严格按照类中声明的顺序来复制。测试
好比:优化
class InitialzationB {
public:
// InitialzationB()
// {}
// InitialzationB(int value): m_IA(value) , m_b(value), m_a(m_b)
// 正宗作法
InitialzationB(int value): m_IA(value), m_a(value), m_b(value)
{
}
private:
InitialzationA m_IA;
int m_b;
int m_a;
};
复制代码
不要在初始化列表中调用成员函数,由于你不知道这个函数之后会多么的依赖当前的对象。spa
如今咱们开始回答上面提出的问题,第一个方法至少消耗了一个ctor,copy ctor, dtor,同时还要考虑编译器的实现,中间可能还会temp object的生成,又会增长一个copy ctor,dtor。反过来再看方法二只消耗了ctor,dtor。效率确定比方法一高。 知道了编译器作了什么,和怎么作的。这将有助于对C++语言背后的实现细节更了若指掌,才能写出高效的程序。同时也看出来c++为了追求效率,背后作了不少咱们不知道的事情。最后假如咱们是编译器,咱们会如何生成代码的?这是值得咱们思考的地方。翻译