论坛
潜水/灌水快乐,沉淀知识,认识更多同行。
ToB圈子
加入IT圈,遇到更多同好之人。
朋友圈
看朋友圈动态,了解ToB世界。
ToB门户
了解全球最新的ToB事件
博客
Blog
排行榜
Ranklist
文库
业界最专业的IT文库,上传资料也可以赚钱
下载
分享
Share
导读
Guide
相册
Album
记录
Doing
搜索
本版
文章
帖子
ToB圈子
用户
免费入驻
产品入驻
解决方案入驻
公司入驻
案例入驻
登录
·
注册
只需一步,快速开始
账号登录
立即注册
找回密码
用户名
Email
自动登录
找回密码
密码
登录
立即注册
首页
找靠谱产品
找解决方案
找靠谱公司
找案例
找对的人
专家智库
悬赏任务
圈子
SAAS
ToB企服应用市场:ToB评测及商务社交产业平台
»
论坛
›
物联网
›
物联网
›
类和对象——拷贝对象时的一些编译器优化 ...
类和对象——拷贝对象时的一些编译器优化
熊熊出没
金牌会员
|
18 小时前
|
显示全部楼层
|
阅读模式
楼主
主题
887
|
帖子
887
|
积分
2661
拷贝对象时的一些编译器优化
在传参和传返回值的过程中,一般编译器会做一些优化(也就是说有的不做优化),减少对象的拷贝,这个在一些场景下照旧非常有效的。
这里只举几个案例,详细见书籍《深度探索c++对象模型》。
在20世纪末盛行的编译器(比方,vc++6.0)不会对这种情况举行优化。
案例1:仅使用类中的成员函数
很多时候,生成这个对象的目的仅仅是为了调用类中的某个函数。此时没须要生成一个对象,特殊是生成一个对象作为实参上传给平凡函数。
#include<iostream>
#include<cstdlib>
using namespace std;
class A {
public:
A(int a = 6)
:a(a) {
cout << "A(int a)" << endl;
}
A(const A& aa)
:a(aa.a) {
cout << "A(const A& aa)" << endl;
}
A& operator=(const A& aa) {
cout << "A& operator=(const A& aa)" << endl;
if (this != &aa) {
a = aa.a;
}
return *this;
}
~A() {
cout << "~A()" << endl;
}
void print() {
using std::cout;
cout << a << "\n";
}
private:
int a;
};
//若调用拷贝构造仅仅是为了调用这个函数,完全没必要传值传参
void f1_1(A a) {
a.print();
}
//所以直接加引用
void f1_2(A& a) {
a.print();
}
void f1() {
A a;
f1_1(a);
cout << endl;
f1_2(a);
cout << endl;
}
int main() {
f1();
return 0;
}
复制代码
案例2:案例1减少一次拷贝构造
首先,const对象不能调用非const成员函数。所以const对象也要准备对应的const函数重载。
其次,引用和const一般在一起,为了避免别名修改原来的对象(变量)。
最后,形参使用引用可以减少一次拷贝构造。
#include<iostream>
#include<cstdlib>
using namespace std;
class A {
public:
A(int a = 6)
:a(a) {
cout << "A(int a)" << endl;
}
A(const A& aa)
:a(aa.a) {
cout << "A(const A& aa)" << endl;
}
A& operator=(const A& aa) {
cout << "A& operator=(const A& aa)" << endl;
if (this != &aa) {
a = aa.a;
}
return *this;
}
~A() {
cout << "~A()" << endl;
}
//相应函数也要对这个类的成员函数进行限制防止权限放大
void print() const {
using std::cout;
cout << a << "\n";
}
private:
int a;
};
//为了支持生成临时对象,使用const引用
void f2_1(const A& a) {
a.print();
}
void f2_2(A& a) {
a.print();
}
void f2_3(A& a) {//非const形参,不具有常属性
a.print();
}
void f2() {
A a;
f2_1(a);//权限缩小
cout << endl;
f2_2(a);//权限平移
cout << endl;
//f2_3(A());//权限放大
f2_1(A());//形参也具有常属性时权限平移,可以调用
cout << endl;
}
int main() {
f2();
return 0;
}
复制代码
输出:
A(int a)
6
6
A(int a)
6
~A()
~A()
复制代码
f2_3(A());无法编译通过,因为临时对象、匿名对象都有常属性,上传无常属性形参的函数,权限放大。
案例3:临时对象也具有常属性
在案例2已经证明匿名对象具有常属性。隐式范例转换的临时对象也具有常属性。
#include<iostream>
#include<cstdlib>
using namespace std;
class A {
public:
A(int a = 6)
:a(a) {
cout << "A(int a)" << endl;
}
A(const A& aa)
:a(aa.a) {
cout << "A(const A& aa)" << endl;
}
A& operator=(const A& aa) {
cout << "A& operator=(const A& aa)" << endl;
if (this != &aa) {
a = aa.a;
}
return *this;
}
~A() {
cout << "~A()" << endl;
}
//相应函数也要对这个类的成员函数进行限制防止权限放大
void print() const {
using std::cout;
cout << a << "\n";
}
private:
int a;
};
//const引用能很好的支持生成临时对象
void f3_1(const A& a) {//这个地方引用和const一般同时出现防止不小心修改
a.print();
}
void f3() {//少调用一次拷贝构造
f3_1(A());//匿名对象有常属性
cout << endl;
f3_1(A(4));
cout << endl;
f3_1(3);//临时对象也具有常属性
cout << endl;
}
int main() {
f3();
return 0;
}
复制代码
输出:
A(int a)
6
~A()
A(int a)
4
~A()
A(int a)
3
~A()
复制代码
它们都被优化成了只调用一次构造函数。
案例4:const引用延伸生命周期
const引用可以延伸临时对象的生命周期,本质是将临时对象变成著名对象,如许临时对象就可以像著名对象一样生命周期在局部。
#include<iostream>
#include<cstdlib>
using namespace std;
class A {
public:
A(int a = 6)
:a(a) {
cout << "A(int a)" << endl;
}
A(const A& aa)
:a(aa.a) {
cout << "A(const A& aa)" << endl;
}
A& operator=(const A& aa) {
cout << "A& operator=(const A& aa)" << endl;
if (this != &aa) {
a = aa.a;
}
return *this;
}
~A() {
cout << "~A()" << endl;
}
//相应函数也要对这个类的成员函数进行限制防止权限放大
void print() const {
using std::cout;
cout << a << "\n";
}
private:
int a;
};
//缺省值为匿名对象
//const延长生命周期使得匿名对象存在于局部
void f4_1(const A& a = A()) {
a.print();
}
void f4() {
f4_1();
cout << endl;
//这里只有ref出了作用域,
//临时对象的生命周期才终止
const A& ref = A();
cout << endl;
ref.print();//还在{}也就是作用域内,可以使用
cout << endl;
}
int main() {
f4();
return 0;
}
复制代码
案例5:传匿名对象传参
编译器优化情况1:隐式范例转换作为实参,此时会调用两次构造。编译器将连续的两次构造(构造+拷贝构造)优化为直接构造。
c++尺度并没有对这种情况举行优化说明,这个实在照旧编译器自己的举动。在一些年代比较久远的编译器(比如20世纪末)就不会。
#include<iostream>
#include<cstdlib>
using namespace std;
class A {
public:
A(int a = 6)
:a(a) {
cout << "A(int a)" << endl;
}
A(const A& aa)
:a(aa.a) {
cout << "A(const A& aa)" << endl;
}
A& operator=(const A& aa) {
cout << "A& operator=(const A& aa)" << endl;
if (this != &aa) {
a = aa.a;
}
return *this;
}
~A() {
cout << "~A()" << endl;
}
//相应函数也要对这个类的成员函数进行限制防止权限放大
void print() const {
using std::cout;
cout << a << "\n";
}
private:
int a;
};
void f5_1(A a) {
a.print();
}//析构
void f5_2(const A a) {
a.print();
}
A f5_3() {
A a;
return a;
}
//隐式类型,连续构造(两次及以上)->优化为直接构造
void f5() {
//传值传参
//正常情况
A a;//构造
f5_1(a);//拷贝构造
cout << endl;
// 一个表达式中,构造+拷贝构造->优化为一个构造
f5_1(A());//匿名对象构造+拷贝构造被优化
cout << endl;
f5_1(A(3));
cout << endl;
f5_1(4);//隐式类型转换
cout << endl;
//这个也是构造+拷贝构造
A b = A(3);
cout << endl;
}
int main() {
f5();
return 0;
}
复制代码
输出:
A(int a)
A(const A& aa)
6
~A()
A(int a)
6
~A()
A(int a)
3
~A()
A(int a)
4
~A()
A(int a)
~A()
~A()
复制代码
分析:
f5_1(A());,f5_1(A(3));:匿名对象调用构造函数,加拷贝构造生成形参。
f5_1(4);:隐式转换,一次构造加拷贝构造。
A b = A(3);:一次构造加拷贝构造。
这三种情况,都被优化为一次构造。
案例6:函数传值返回时的优化
#include<iostream>
#include<cstdlib>
using namespace std;
class A {
public:
A(int a = 6)
:a(a) {
cout << "A(int a)" << endl;
}
A(const A& aa)
:a(aa.a) {
cout << "A(const A& aa)" << endl;
}
A& operator=(const A& aa) {
cout << "A& operator=(const A& aa)" << endl;
if (this != &aa) {
a = aa.a;
}
return *this;
}
~A() {
cout << "~A()" << endl;
}
//相应函数也要对这个类的成员函数进行限制防止权限放大
void print() const {
using std::cout;
cout << a << "\n";
}
private:
int a;
};
A f6_1() {
A a;//构造
return a;//拷贝构造生成临时对象
}
A& f6_2() {
A a;
return a;
}
void f6() {
A a;
cout << endl;
f6_1();
cout << endl;
a = f6_1();
cout << endl;
A ret = f6_1();
cout << endl;
A ret2 = f6_2();
cout << endl;
}
int main() {
f6();
return 0;
}
复制代码
输出:
A(int a)
A(int a)
A(const A& aa)
~A()
~A()
A(int a)
A(const A& aa)
~A()
A& operator=(const A& aa)
~A()
A(int a)
A(const A& aa)
~A()
A(int a)
~A()
A(const A& aa)
~A()
~A()
~A()
复制代码
单独看A ret = f6_1();这种情况:
A f6_1()在return语句会生成临时对象,但编译器举行了优化,直接将这个a在生命周期结束前拷贝给ret。
所以在一个表达式的连续两个步调里,局部对象构造 + 传值返回生成临时对象调用拷贝构造,两次调用构造被优化为一次。
而A ret2 = f6_2();因为f6_2是传引用返回,所以直接省去了return语句的一次拷贝构造,在析构前生成临时对象,之后通过拷贝构造将对象拷贝给ret2。
案例7:优化的条件
#include<iostream>
#include<cstdlib>
using namespace std;
class A {
public:
A(int a = 6)
:a(a) {
cout << "A(int a)" << endl;
}
A(const A& aa)
:a(aa.a) {
cout << "A(const A& aa)" << endl;
}
A& operator=(const A& aa) {
cout << "A& operator=(const A& aa)" << endl;
if (this != &aa) {
a = aa.a;
}
return *this;
}
~A() {
cout << "~A()" << endl;
}
//相应函数也要对这个类的成员函数进行限制防止权限放大
void print() const {
using std::cout;
cout << a << "\n";
}
private:
int a;
};
A f7_1() {
A a;
return a;
}
void f7() {//这种情况编译器不会再优化
A ret2;
ret2 = f7_1();
}
int main() {
f7();
return 0;
}
复制代码
f7()这种情况不能优化,两个缘故原由:
同范例才气优化(都是构造或都是拷贝构造才气优化,这里是构造和赋值)。
不在同一步调(声明对象和赋值重载是两个语句或者说步调)。
案例8:隐式范例转换的优化
和案例6的情况相似,都是构造临时对象并返回,只是存在隐式范例转换。所以被优化为一次构造。
#include<iostream>
#include<cstdlib>
using namespace std;
class A {
public:
A(int a = 6)
:a(a) {
cout << "A(int a)" << endl;
}
A(const A& aa)
:a(aa.a) {
cout << "A(const A& aa)" << endl;
}
A& operator=(const A& aa) {
cout << "A& operator=(const A& aa)" << endl;
if (this != &aa) {
a = aa.a;
}
return *this;
}
~A() {
cout << "~A()" << endl;
}
//相应函数也要对这个类的成员函数进行限制防止权限放大
void print() const {
using std::cout;
cout << a << "\n";
}
private:
int a;
};
//被优化为直接构造
//构造匿名对象加临时对象,两次构造被优化为1次
A f8_1() {
return A();
}
A f8_2() {
return 8;
}
A f8_3() {
return A(1);
}
void f8() {
A a1 = f8_1();
cout << endl;
A a2 = f8_2();//隐式类型转换
cout << endl;
A a3 = f8_3();
cout << endl;
}
int main() {
f8();
return 0;
}
复制代码
所以就有了如许一个特性:局部对象都只能传值返回,因此可以的话尽大概使用临时对象返回或隐式范例转换,可以减少拷贝调用次数。
再次理解封装
现实生活中的实体计算机并不认识,计算机只认识二进制格式的数据。如果想要让计算机认识现实生活中的实体,用户必须通过某种面向对象的语言,对实体举行描述,然后通过编写步伐,创建对象后计算机才可以认识。
比如想要让计算机认识洗衣机,就需要:
用户先要对现实中洗衣机实体举行抽象——即在人为头脑层面对洗衣机举行认识,洗衣机有什么属性,有那些功能,即对洗衣机举行抽象认知的一个过程。
经过1之后,在人的头脑中已经对洗衣机有了一个苏醒的认识,只不外此时计算机还不清楚,想要让计算机识别人想象中的洗衣机,就需要人通过某种面向对象的语言(比如:c++、java、python等)将洗衣机用类来举行描述,并输入到计算机中。
经过2之后,在计算机中就有了一个洗衣机类,但是洗衣机类只是站在计算机的角度对洗衣机对象举行描述的,通过洗衣机类,可以实例化出一个个详细的洗衣机对象,此时计算机才气洗衣机是什么东西。
用户就可以借助计算机中洗衣机对象,来模拟现实中的洗衣机实体了。
所以
类是对某一类实体
(
对象
)
来举行描述的
,描述该对象具有那些
属性
,那些
方法
,描述完成后就
形成
了一种新的
自定义范例
,才用该自定义范例就可以实例化详细的对象。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
本帖子中包含更多资源
您需要
登录
才可以下载或查看,没有账号?
立即注册
x
回复
使用道具
举报
0 个回复
倒序浏览
返回列表
快速回复
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
or
立即注册
本版积分规则
发表回复
回帖并转播
回帖后跳转到最后一页
发新帖
回复
熊熊出没
金牌会员
这个人很懒什么都没写!
楼主热帖
数据库的三大范式
在字节跳动干软件测试5年,4月无情被辞 ...
创建SQL server服务器
TortoiseGit间接处理linux目录下的仓库 ...
django小项目 ----- 创建错题集 ...
神经网络与深度学习
[Golang] GO 语言工作环境的基本概念 ...
Java如何调用HttpURLConnection类模拟 ...
ZYNQ从放弃到入门(十二)- AMP — Zyn ...
SeaTunnel 发布成为 Apache 顶级项目后 ...
标签云
存储
挺好的
服务器
浏览过的版块
.Net
快速回复
返回顶部
返回列表