C++对象优化

本文深入探讨了C++中对象的优化策略,包括函数返回对象优化、类型转换时临时对象的生命周期、指针引用与临时对象的关系,以及构造析构顺序。同时,介绍了如何通过右值引用解决临时对象拷贝构造问题,提升程序效率。并举例说明了在vector等容器中应用移动语义的实现方式。
摘要由CSDN通过智能技术生成

函数返回对象优化

#include <iostream>

using namespace std;

class Test {
public:
	Test(int a = 10) :ma(a) {
		cout << "Test(int)" << endl;
	}
	Test(const Test& src):ma(src.ma) {
		cout << "Test(const Test& src)" << endl;
	}
	Test& operator=(const Test& src) {
		cout << "operator=" << endl;
		ma = src.ma;
		return *this;
	}
	~Test() {
		cout << "~Test()" << endl;
	}
private:
	int ma;
};

int main() {
	Test t1;
	Test t2(t1);
	Test t3 = t1;
	// Test(20)显示生成临时对象  生命周期:所在语句
	// 被优化为 Test t4(20)
	Test t4 = Test(20);
	cout << "------------" << endl;
	return 0;
}

重点:用临时对象拷贝构造新对象的时候,临时对象不构造了,直接生成目标对象
在这里插入图片描述

类型转换时的临时对象

	// 这句调用operator=,必须生成临时对象,出了这条语句后,临时对象被析构
	t4 = Test(30);
	// 显式(生成临时对象)类型转换int->Test,编译器会查看Test是否存在一个合适的构造函数,存在就直接构造
	t4 = (Test)40;
	// 隐式(生成临时对象)类型转换int->Test,出了该语句就被析构
	t4 = 50;

指针、引用、临时对象

	// 出了这条语句后,临时对象被析构,ptr成为野指针
	Test* ptr = &Test(10);
	// 引用相当于给了临时对象一个名字,这个临时对象的生命周期延长为这个引用变量的周期
	const Test& ref = Test(10);

构造析构顺序

构造的时候先构造全局变量、然后进入函数顺序执行(执行到static时才初始化)
析构的时候,先析构局部变量,然后是static变量和全局变量

#include <iostream>

using namespace std;

class Test {
public:
	Test(int a = 5, int b = 5) :ma(a), mb(b) {
		cout << "Test(int, int)" << endl;
	}
	Test(const Test& src):ma(src.ma), mb(src.mb) {
		cout << "Test(const Test& src)" << endl;
	}
	void operator=(const Test& src) {
		cout << "operator=" << endl;
		ma = src.ma;
		mb = src.mb;
	}
	~Test() {
		cout << "~Test()" << endl;
	}
private:
	int ma;
	int mb;
};

// 全局变量最先初始化
Test t1(10, 10); // 1. Test(int, int)

int main() {
	Test t2(20, 20);  // 3. Test(int, int)
	Test t3 = t2;     // 4. Test(const Test& src)

	// static变量,运行到这条语句才初始化
	// 临时对象构造新对象,直接优化为构造目标对象 
	static Test t4 = Test(30, 30); // 5. Test(int, int)

	t2 = Test(40, 40);     // 6. Test(int, int)  operator=  ~Test()
	t2 = (Test)(50, 50);   // 7. Test(int, int)  operator=  ~Test()       逗号表达式 :同t2 = (Test)50;
	t2 = 60;               // 8. Test(int, int)  operator=  ~Test()
	 
	Test* p1 = new Test(70, 70);   // 9. Test(int, int) 
	Test* p2 = new Test[2];        // 10. Test(int, int)  Test(int, int) 
	Test* p3 = &Test(80, 80);      // 11. Test(int, int)  ~Test()
	const Test& p4 = Test(90, 90); // 12. Test(int, int)

	delete p1;                     // 13. ~Test()
	delete[] p2;                   // 14. ~Test() ~Test()

	// 最后析构顺序:p4、t3、t2、t4、t5、t1(局部变量、静态变量、全局变量)
	return 0;
}

Test t5(10, 10); // 2. Test(int, int)

函数调用过程

#include <iostream>

using namespace std;

class Test {
public:
	Test(int a = 5) :ma(a){
		cout << "Test(int, int)" << endl;
	}
	Test(const Test& src):ma(src.ma){
		cout << "Test(const Test& src)" << endl;
	}
	void operator=(const Test& src) {
		cout << "operator=" << endl;
		ma = src.ma;
		// return *this;
	}
	~Test() {
		cout << "~Test()" << endl;
	}
	int get_data() {
		return ma;
	}
private:
	int ma;
};

// 返回值为Test*和Test&都不对,不能返回局部对象指针和引用(针对栈区数据,栈帧清退,对象销毁。指向堆区和数据区的指针可以返回)
Test get_obj(Test t) {      // 3. Test(const Test& src) 实参拷贝构造形参
	int val = t.get_data();
	Test tmp(val);          // 4. Test(int)
	                        // 5. Test(const Test& src) 用tmp在main函数栈帧上拷贝构造临时对象
							// 6. 析构tmp ~Test() 7. 析构形参t ~Test() 
	return tmp;
}

int main() {
	Test t1;              // 1. Test(int)
	Test t2;              // 2. Test(int)
	t2 = get_obj(t1);     // 8. t2.operator=(临时对象) 
						  // 9. 临时对象析构
						  // 10. t2析构、t1析构
	return 0;
}

优化后的代码:

Test get_obj(Test& t) {     
	return(t.get_data());
}

int main() {
	Test t1;          
	Test t2 = get_obj(t1);			  
	return 0;
}

在这里插入图片描述

总结对象优化规则:

  1. 函数参数传递过程中,对象优先用引用传递,不要按值传递。避免实参形参的拷贝构造以及出作用域的析构
  2. 用临时对象拷贝构造一个新对象的时候,C++编译器直接优化为构造目标对象。所以被调用函数返回的时候直接返回临时对象即可(此时被调用函数的栈帧上不会产生临时对象,也就不用出作用域析构),无需先构造后返回。
  3. 接收一个对象返回值的时候,以初始化(拷贝构造)的方式接收,而不是以赋值的方式接收
#include <iostream>
using namespace std;

class String
{
public:
    String(const char* str = nullptr)
    {
        cout << "String(const char*)" << endl;
        if (str != nullptr)
        {
            m_data = new char[strlen(str) + 1];
            strcpy(m_data, str);
        }
        else
        {
            m_data = new char[1];
            *m_data = '\0';
        }
    }
    String(const String& src)
    {
        cout << "String(const String& src)" << endl;
        m_data = new char[strlen(src.m_data) + 1];
        strcpy(m_data, src.m_data);
    }
    ~String()
    {
        cout << "~String()" << endl;
        delete[]m_data;
        m_data = nullptr;
    }

    //调用String&是为了支持连续的operator=赋值操作
    String& operator=(const String& src)
    {
        cout << "operator=" << endl;
        if (&src == this)
        {
            return *this;
        }
        delete[]m_data;
        m_data = new char[strlen(src.m_data) + 1];
        strcpy(m_data, src.m_data);
        return *this;
    }

    const char* c_str() {
        return m_data;
    }
private:
    char* m_data;//用于保存字符串
};

String get_string(String& str) {
    const char* pstr = str.c_str();
    String tmp(pstr);
    return tmp;
}

int main(){
    String str1("11111111111111111");
    String str2;
    str2 = get_string(str1);
    cout << str2.c_str() << endl;
    return 0;
}
*/

在这里插入图片描述

使用右值引用解决临时对象拷贝构造等问题

	 // 临时对象(右值)会匹配到右值引用为参数的函数,这里的src是临时对象(右值)
    String(String&& src){
        cout << "String(const String&& src)" << endl;
        m_data = src.m_data;
        src.m_data = nullptr;
    }
    
    String& operator=(String&& src){
        cout << "operator=(String&& src)" << endl;
        if (&src == this)
        {
            return *this;
        }
        delete[]m_data;
        m_data = src.m_data; // 改变堆区资源指向
        src.m_data = nullptr; // 临时对象的指针置空,防止析构的时候释放资源
        return *this;
    }

vector上应用String
在这里插入图片描述

std::move:移动语义,得到右值类型
std::forward:类型的完美转发,得到真实的左/右值

函数模板的类型推演 + 引用折叠:

  1. String&& + && = String&&
  2. String& + && = String&
   //void construct(T* p, const T& val)//负责对象构造
    //{
    //    new(p)T(val);//定位new
    //}
    //void construct(T* p, T&& val)//负责对象构造
    //{
    //    new(p)T(std::move(val));//定位new
    //}
    

    template<typename Ty2>
    void construct(T* p, Ty2&& val)//负责对象构造
    {
        new (p) T(std::forward<Ty2>(val));//定位new
    }


	
    //void push_back(const T& val)//向容器末尾添加元素
    //{
    //    if (full())
    //        expand();
    //    //*_last++ = val;//last指针指向的内存构造一个值为val的对象
    //    _allocator.construct(_last, val);
    //    _last++;

    //}
    //void push_back(T&& val)//向容器末尾添加元素
    //{
    //    if (full())
    //        expand();
    //    //*_last++ = val;//last指针指向的内存构造一个值为val的对象
    //    _allocator.construct(_last, std::move(val));
    //    _last++;
    //}
    
    // 函数模板的类型推演 + 引用折叠
    // String&& + && = String&&
    // String& + && = String&
    // T char
    // Ty1 String

    template<typename Ty1> // 换个名会进行类型推演,直接用T直接断定是右值引用
    void push_back(Ty1&& val) {
        if (full()) {
            expand();
        }
        // forward:类型的完美转发
        _allocator.construct(_last, std::forward<Ty1>(val));
        _last++;
    }
    
    // 函数模板的类型推演 + 引用折叠
    // String&& + && = String&&
    // String& + && = String&
    template<typename Ty1>
    void push_back(Ty1&& val) {
        if (full()) {
            expand();
        }
        // forward:类型的完美转发
        _allocator.construct(_last, std::forward<Ty1>(val));
        _last++;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bugcoder-9905

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值