C++对象构造和对象优化


一、对象的构造和析构

#include "iostream"

class Test {
public:
    Test(int data = 0) : ma(data) { std::cout << "default construction" << std::endl; }

    Test(const Test &t) : ma(t.ma) { std::cout << "copy construction" << std::endl; }

    Test &operator=(const Test &t) {
        ma = t.ma;
        return *this;
        std::cout << "operator=" << std::endl;
    }

private:
    int ma;
};

int main() {
    Test t1;
    Test t2(t1);
    Test t3 = t1;
    //Test(20)的是临时对象,临时对象的生存周期就是当前表达式
    //未优化是,按照逻辑会先调用默认构造函数,然后再调用拷贝构造函数
    //C++编译器对对象构造的优化:用临时对象构造新对象的时候,临时对象不产生,直接构造新对象(对象优化的原理)
    Test t4 = Test(20);
    //显式生成临时对象
    //以下语句,赋值的时候临时对象是会产生的,临时对象产生后再调用赋值运算符重载
    t4 = Test(40);
    //类型强转,编译器会使用带int参数的构造函数进行转换
    t4 = (Test) 40;
    //隐式生成临时对象
    t4 = 40;
    //以下语句不合法,因为出了语句,临时对象就不存在了,不能被赋值给指针
    Test *p = &Test(40);
    //可以用常引用指向临时对象
    const Test &ref = Test(50);
    system("pause");
    return 0;
}

 
对象的各种构造函数:

  1. 默认构造函数;
  2. 带左值引用参数的拷贝构造函数;
  3. 带右值引用参数的拷贝构造函数。

分析对象构造注意事项:

  1. 区分赋值和对象初始化,赋值调用的是operator=,初始化调用的拷贝构造函数;
  2. 区分带左值引用的拷贝构造函数和带右值引用的拷贝构造函数,由于右值本身也是左值,此时需要使用forward;
  3. 区分函数调用和对象构造的区别。TestClass a(),这是函数的定义,TestClass a=TestClass(),这是对象的构造。

 

二、对象优化规则和原理分析

原理分析:

使用值传递给函数传参,需要使用实参给形参进行初始化,相当于形参对象的拷贝构造,这个过程会构造对象。函数返回返回值时,由于不能直接把返回值保存下来(函数运行结束,返回值所在栈内存就被回收了),会先使用返回值构造一个临时对象,然后用临时对象进行赋值或者构造。
 
优化规则:

  1. 函数参数传递过程中,对象优先按照引用传递,这样可以减少参数传递过程中对象的构造;
  2. 当函数返回对象的时候,应该优先返回一个临时对象,而不要返回一个定义过的对象;
  3. 接受返回值是对象的函数调用的时候,优先按照初始化的方式接受,不要按赋值的方式接收。
     

三、左值和右值,左值引用和右值引用

  • 左值:有内存,有名字,且可以明确的可识别的内存位置;
  • 右值:无内存(如立即数存在于寄存器中)或者无名字,内存不可明确识别(比如临时变量);
  • 左值引用&:只能引用左值;
  • 右值引用&&:只能引用右值,本质还是产生了临时变量,然后再引用。一个右值引用变量本身上也是一个左值,不能用右值变量再引用。
//int tmp=20;
//const int &a = tmp;
//以下语句本质上是产生了临时变量如上
const int &a = 20;
    
//int tmp=20;
//int &b = tmp;
//以下语句本质上是产生了临时变量如上
int &&b = 20;

 

四、对拷贝构造函数和赋值重载的修改

由于使用临时对象给某个对象赋值或者构造的时候,临时对象可能会开辟资源,赋值给对象的之后又要被释放,但是临时对象开辟的资源和被赋值的对象是相同的,我们可以直接让被赋值的对象指向临时对象开辟的资源,避免多次的开辟和释放相同内容的资源。

class Test{
public:
    
    Test(const Test &t){}
    //增加带右值引用参数的拷贝构造函数
    Test(const Test &&t){}
    Test& operator=(const Test &t){}
    //增加带右值引用参数的赋值重载函数
    Test& operator=(const Test &&t){}
};

 

五、move和forward语义

  1. move
int &&a = 10;
std::move(a); //将左值转换成右值
  1. 模板和引用折叠相结合

利用模板的实参推演

template<typename T>
void test(T &&val) {
    cout << val << endl;
    cout << __FUNCTION__ << endl;
}
int main() {
    int a = 100;
    test(a);  //引用折叠,& + && = &,此时test函数中的val就是左值
    test(10); //&& + && = &&,此时test函数中的val就是右值
    system("pause");
    return 0;
}
  1. forward
std::forward<T>(val) //类型完美转发,能够识别左值和右值
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值