优化对象变高效

简介: 没有优化过的对象,不足以看出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个

相关文章
|
9月前
|
存储 安全 Java
深度解析ArrayList:灵活高效的动态数组实现
在 Java 集合框架中,ArrayList 是一个常用而强大的类,它提供了动态数组的实现,允许在运行时动态调整数组的大小。 ArrayList 是 List 接口的实现类,基于动态数组的数据结构。它可以存储任意类型的对象,并提供了丰富的方法,包括添加、删除、遍历等,使其在各种场景下都能发挥重要作用。
155 1
深度解析ArrayList:灵活高效的动态数组实现
|
9月前
对象的优化
对象的优化
|
4月前
|
存储 缓存 监控
如何提高数据驱动方式的性能和可维护性?
【10月更文挑战第13天】 本文深入探讨了提高数据驱动方式性能与可维护性的关键方法和策略,包括优化数据结构选择、数据缓存策略、合理的数据更新策略、数据压缩与精简、代码结构优化、测试与监控、版本控制与协作管理、文档化与知识共享、持续优化的意识及结合实际案例分析,旨在为数据驱动的高效和可持续发展提供全面指导。
|
3月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
99 1
|
3月前
|
缓存 算法 JavaScript
_.isEqual 方法在处理大型对象时的性能如何?
【10月更文挑战第29天】`_.isEqual` 方法在处理大型对象时性能存在一定的挑战,但通过其自身的优化机制以及结合适当的优化策略,仍然能够在许多场景下满足对大型复杂对象进行深度比较的需求。在实际使用中,需要根据具体情况综合考虑性能和功能的平衡,以选择最合适的比较方法。
|
4月前
|
缓存
在数据驱动方式中处理复杂的数据结构
【10月更文挑战第13天】 在数据驱动的开发模式中,处理复杂数据结构是一项重要任务。本文从理解特性、数据分解、选择模型、数据绑定、转换预处理、处理嵌套、性能优化、错误处理、数据验证及实际案例等方面,详细阐述了应对这一挑战的方法和策略,强调了持续学习和改进的重要性。
|
6月前
|
SQL 缓存 监控
优化大型数据库查询的最佳实践
在处理大规模数据时,数据库查询性能的优化至关重要。本文探讨了几种优化大型数据库查询的最佳实践,包括索引策略、查询重写、数据分区和缓存机制。通过这些方法,开发人员可以显著提高查询效率,减少系统负担,提升用户体验。本文还结合实际案例,提供了具体的优化技巧和工具建议,帮助读者有效地管理和优化大型数据库系统。
|
9月前
|
存储 缓存 安全
【C/C++ 项目优化实战】 分享几种基础且高效的策略优化和提升代码性能
【C/C++ 项目优化实战】 分享几种基础且高效的策略优化和提升代码性能
426 0
|
9月前
|
安全 编译器 程序员
C++14特性:解锁现代C++功能以获得更具表现力和更高效的代码
C++14特性:解锁现代C++功能以获得更具表现力和更高效的代码
171 0
希望大家通过正确的方法高效学习。
希望大家通过正确的方法高效学习。