优化对象变高效

简介: 没有优化过的对象,不足以看出C++的优势

引言

函数对象,以及其构造函数等,在C++中经常用,所以这篇文章就分析一下其中的调用以及如何优化

函数在使用过程调用了哪些方法

#include<iostream>
using namespace std;
class Test {
public:
  Test(int a = 10) : ma(a)
  {
    cout << "Test(int)" << endl;
  }
  ~Test()
  {
    cout << "~Test()" << endl;
  }
  Test(const Test& t) :ma(t.ma)
  {
    cout << "Test(const Test&)" << endl;
  }
  Test& operator=(const Test& t)
  {
    cout << "operator=" << endl;
    ma = t.ma;
    return *this;
  }
private:
  int ma;
};
int main()
{
  Test t1;
  Test t2(t1);
  Test t3 = t1;
    // Test(20)是显式生成临时对象,生存周期为所在的语句
    /*
    C++编译器对于对象构造的优化:用临时对象生成新对象时,临时对象就不产生了,直接构造新对象就行
    */
  Test t4 = Test(20);// Test t4(20) 没有任何区别
  cout << "------------" << endl;
  t4 = t2; // t4.operator = (const Test & t);
  // 显式生成临时对象
    /*
    因为这时候t4已经有了,所以再用临时对象的话,临时对象就要产生
    */
  t4 = Test(30);
  t4 = (Test)30;
  // 隐式生成临时对象
  t4 = 30; // Test(30) int->Test(int)
  cout << "------------" << endl;
  return 0;
}

运行结果为:

t4 = Test(30);是先临时生成一个对象所以调用了Test(int),这个临时对象再赋值给t4,所以调用了operator=,临时对象再析构,所以是~Test()

下面这三行代码,都是

Test(int)

operator=

~Test()

// 显式生成临时对象
t4 = Test(30);
t4 = (Test)30;
// 隐式生成临时对象
t4 = 30;

只不过前两个是显式,因为指明了Test,只不过从Test里面找int,最后一个是隐式,推断t4的类Test,再通过30这个int,找Test里面的int

前两个没有任何区别

这时再在上面的代码最后再加上这样的代码

p指向的是一个已经析构的临时对象
Test *p = &Test(40);
const Test &ref = Test(50);

此时输出会显示为

Test(int)  临时对象产生
~Test()   临时对象析构,当Test *p = &Test(40);这一句执行后,就被析构,所以此时指针已经无效了,因为临时对象已经被析构
Test(int) 引用的临时对象
~Test() 当main函数结束时,被析构

临时对象是没有名字的,所以过了这一句的生命周期,用指针指的话,就为空了

但是引用相当于别名,是把这块内存安了个名字,在这里,只有当引用对象什么时候出作用域,它才被析构

所以用指针指向临时对象是不安全的

再看两个

Test *p1 = new Test(10); // Test(int) 此时不是临时对象因为用的new,在堆上分配,而上面的是在栈上
Test *p1 = new Test[2]; // Test(int) Test(int)两次构造
delete p1; // ~Test()
delete []p2;// ~Test()  ~Test()

函数调用过程中,对象背后调用的方法太多

// 正确写法
Test GetObject(Test t)
{
  int val = t.getData();
  Test tmp(val);
  return tmp;
}
// 错误写法
Test* GetObject(Test t)
{
  int val = t.getData();
  Test tmp(val);
  return &tmp;
}
/*
因为这里的tmp是在函数里的局部对象,所以就算tmp传递一个指针,我们通过返回值知道地址后,但是这个函数运行结束后,这个内存就没了,所以这个对象的内存也就没了,那你知道了他的地址也没用了,所以不能返回局部的或者临时对象的指针或者引用
*/
// 正确写法
Test* GetObject(Test t)
{
  int val = t.getData();
  static Test tmp(val);
  return &tmp;
}
/*
这样就可以,因为static是一开始就分类的内存,他一直都在
*/
#include <iostream>
using namespace std;
Class Test
{
public:
    Test(int data = 10) :ma(data) //初始化ma的值
    {
        cout << "Test(int)" << endl;
    }
    ~Test()
    {
        cout << "~Test()"<< endl;
    }
    Test(const Test& t) :ma(t.ma)
    {
        cout << "Test(const Test&)" << endl;
    }
private:
    int ma;
};
Test GetObject(Test t)
{
  int val = t.getData();
  Test tmp(val);
  return tmp;
}
int main()
{
    Test t1;
    Test t2;
    t2 = GetObject(t1);
    return 0;
}

这三行代码,其实调用了11个函数

三条对象优化的规则

1.函数参数传递过程中,对象优先按引用传递,不要按值传递

2.函数返回对象的过程中,应该优先返回一个临时对象,而不要返回一个定义过的对象

3.接收返回值是对象的函数调用的时候,优先按初始化的方式接受,不要按赋值的方式接收

把上面的代码优化成这样

Test GetObject(Test &t) // 按引用传递,这样这里就少了一个构造
{
  int val = t.getData();
  //Test tmp(val);
  //return tmp;
    return Test(val); // 返回临时对象,这样少了上面两行的构造
}
int main()
{
    Test t1;
    Test t2 = GetObject(t1); //按初始化的方式接受
    //t2 = GetObject(t1);
    return 0;
}

这是第二条优化

这是第三条:

优化后,函数调用从11个变成了4个

相关文章
|
运维 算法 Linux
LVS详解(四)——LVS安装与配置命令
LVS详解(四)——LVS安装与配置命令
371 2
|
Java 数据库 Spring
【Spring】资源操作管理:Resource、ResourceLoader、ResourceLoaderAware;
【Spring】资源操作管理:Resource、ResourceLoader、ResourceLoaderAware;
254 1
|
自然语言处理 监控 Cloud Native
精华推荐 |【深入浅出Sentinel原理及实战】「原理探索专题」完整剖析Alibaba微服务架构体系之轻量级高可用流量控制组件Sentinel(1)
精华推荐 |【深入浅出Sentinel原理及实战】「原理探索专题」完整剖析Alibaba微服务架构体系之轻量级高可用流量控制组件Sentinel(1)
1709 0
精华推荐 |【深入浅出Sentinel原理及实战】「原理探索专题」完整剖析Alibaba微服务架构体系之轻量级高可用流量控制组件Sentinel(1)
|
关系型数据库 分布式数据库 数据库
PolarDB 入门
PolarDB 入门
274 0
|
数据采集 中间件 Python
Scrapy爬虫:利用代理服务器爬取热门网站数据
Scrapy爬虫:利用代理服务器爬取热门网站数据
|
12月前
|
监控 负载均衡 JavaScript
PM2 介绍
【10月更文挑战第11天】
|
分布式计算 Hadoop 大数据
Spark 与 Hadoop 的大数据之战:一场惊心动魄的技术较量,决定数据处理的霸权归属!
【8月更文挑战第7天】无论是 Spark 的高效内存计算,还是 Hadoop 的大规模数据存储和处理能力,它们都为大数据的发展做出了重要贡献。
198 2
|
JSON 前端开发 JavaScript
技术笔记:vmware之VMwareRemoteConsole(VMRC)SDK(二)
技术笔记:vmware之VMwareRemoteConsole(VMRC)SDK(二)
625 0
|
API Go
使用Go语言通过API获取代理IP并使用获取到的代理IP
使用Go语言通过API获取代理IP并使用获取到的代理IP
180 1
|
弹性计算 应用服务中间件 nginx
通过workbench远程登录ECS,快速搭建Docker环境
本教程指导用户体验通过workbench远程登录ECS,完成搭建Docker环境的快速搭建,并使用Docker部署一个Nginx服务。