189 8069 5689

1.对象的引用优化,右值引用优化

这一节中主要讲了对象和函数在使用和调用过程中一些注意事项,比较重要的是右值引用和最后的move和forward

网站建设哪家好,找创新互联!专注于网页设计、网站建设、微信开发、重庆小程序开发、集团企业网站建设等服务项目。为回馈新老客户创新互联还提供了班玛免费建站欢迎大家使用!

对象的使用过程中调用了哪些方法?

对于以下这个测试类,列出了十几种不同的定义方式

class Test {
public:
    Test(int a = 4, int b = 10) : ma(a), mb(b) {
        cout << "Test()" << endl;
    }

    ~Test() {
        cout << "~Test()" << endl;
    }

    Test(const Test &src) {
        ma = src.ma;
        mb = src.mb;
        cout << "Test(const Test&)" << endl;
    }

    Test &operator=(const Test &src) {
        ma = src.ma;
        mb = src.mb;
        cout << "operator=(const Test&)" << endl;
        return *this;
    }

private:
    int ma;
    int mb;
};

实现结果如下:

有几个比较值得注意的点:

  • 对象赋值的情况会产生临时对象,临时对象在语句结束后会执行析构函数
  • 隐式生成的临时对象,如t2=60,编译器会找对象中有无合适的构造方法生成对象
  • 用指针保存临时对象,临时对象在语句结束后会被析构。安全的做法是通过引用指向对象
  • (50,50)这种形式的是逗号表达式,赋值的时候只看最后的数字

函数调用过程中背后调用的方法

函数调用的过程中,实参传递到形参需要重新初始化,函数的形参对象需要初始化,这个过程中会调用对象的拷贝构造方法

函数体内部返回的对象也要现在main栈帧中拷贝构造一个临时变量,才能在main作用域中访问这个对象。

函数体执行完毕后需要先析构函数体内构造的对象,然后再析构形参列表构造的对象

三条对象优化的规则

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

上图中的代码最后被优化为以下代码:

Test GetObject(Test &t){
	int val=t.getData();
	return Test(val);//定义临时对象 2.Test()
}
int main(){
	Test t1;//1.Test()
	Test t2=GetObject(t1);//用临时对象拷贝构造同类型的新对象,编译器会优化此过程 少了临时对象在main栈帧上的构造和析构
	return 0;
}
//3.~Test()
//4.~Test()

优化完只剩下4步构造析构的过程

之前String代码中的问题

class String {

    friend std::ostream &operator<<(std::ostream &os, const String &src);
    friend String operator+(const String &l, const String &r);

public:
    String(const char *src = nullptr) {
        if (src == nullptr) {
            _pstr = new char[1];
            *_pstr = '\0';
        } else {
            _pstr = new char[strlen(src) + 1];
            strcpy(_pstr, src);
        }
        std::cout<<"String(const char *src = nullptr)"<(const String &str) const {
        return strcmp(_pstr, str._pstr) > 0;
    }

    bool operator<(const String &str) const {
        return strcmp(_pstr, str._pstr) < 0;
    }

    bool operator==(const String &str) const {
        return strcmp(_pstr, str._pstr) == 0;
    }

    int length() const {
        return strlen(_pstr);
    }

    char &operator[](int index) {
        return _pstr[index];
    }

    char *c_str() const {
        return _pstr;
    }

private:
    char *_pstr;
};

String GetString(String& str){
    const char* pstr=str.c_str();
    String tmpStr(pstr);
    return tmpStr;//这一步要在main栈帧中拷贝构造一个临时变量,会重新划分一块内存
}

int main(){
   	String s1("assf");
    String s2;
    s2=GetString(s1);//调用赋值重载函数,会删除原有内存,重新划分一块内存
    cout<

在调用中出现了多次临时对象,产生一个临时对象就要在栈帧上拷贝赋值原来的内存,而使用一次就要删除,非常耗时

添加带右值引用参数的拷贝构造和赋值函数

一个右值引用变量本身是一个左值,所以一个定义好的右值引用变量不能赋值给右值引用

带右值引用参数的拷贝构造函数和赋值重载函数会指向临时对象开辟的内存,在整个过程中不会有无效的内存释放和开辟,大幅提高了运行效率

实例代码如下:

//带右值引用的拷贝构造函数
String(String &&src)  noexcept {
    std::cout<<"String(String &&)"<

输出结果如下:

带有左值引用的拷贝重载的对象中一般都有带右值引用的拷贝重载的版本。

自定义的String类在vector中的应用

在push_back的过程中,如果传入左值,匹配带有左值参数的临时对象,如果传入临时对象,会首先调用临时对象的构造函数,再调用带右值参数的拷贝构造函数。

为什么push_back会调用带有右值引用的拷贝构造函数?看下面 \(\Downarrow\)

move移动语义和forward类型完美转发

move()是将左值转化为右值

forward()是指类型的完美转发,能够识别左值和右值类型

如果在自己定义的vector类里定义支持调用右值引用的push_back方法,首先要push_back的参数是一个右值引用的类型

第一种写法:使用函数重载,分别定义一个参数是左值引用的和一个参数是右值引用的函数

void push_back(T &val) {
    if (full()) {
        expend();
    }
    //*_last++ = val;
    _alloctor.construct(_last, val);
    _last++;
}

void push_back(T &&val) {
    if (full()) {
        expend();
    }
    _alloctor.construct(_last, std::move(val));
    _last++;
}

在函数钟调用了_alloctor.construct(),该函数传递了参数val,所以也需要函数重载接受右值引用和左值引用。

void construct(T *p, const T &val) {//负责对象构造
    new(p) T(val);//定位new
}

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

第二种写法:使用函数模板的类型推演和引用折叠

首先说明引用折叠是什么意思。如果函数模板推演出的类型是Ty&& + &&(+后面的&&是参数中,属于必带的符号),引用折叠后的类型就就是Ty&&,是右值引用;如果函数模板推演出的类型是Ty& + &&,引用折叠后的类型就就是Ty&,是左值引用。使用forward可以识别Ty的左值或者右值的类型。

template
void push_back(Ty &&val) {//Ty识别传入参数是左值还是右值,然后进行引用折叠
    if (full()) {
        expend();
    }
    _alloctor.construct(_last, std::forward(val));//将val转换为Ty识别到的类型,避免使用函数重载
    _last++;
}
template
void construct(T *p, Ty &&val) {//Ty识别传入参数是左值还是右值,然后进行引用折叠
    new(p) T(std::forward(val));//将val转换为Ty识别到的类型,避免使用函数重载
}

分享题目: 1.对象的引用优化,右值引用优化
当前网址:http://cdxtjz.cn/article/dsoipso.html

联系我们

您好HELLO!
感谢您来到成都网站建设公司,若您有合作意向,请您为我们留言或使用以下方式联系我们, 我们将尽快给你回复,并为您提供真诚的设计服务,谢谢。
  • 电话:028- 86922220 18980695689
  • 商务合作邮箱:631063699@qq.com
  • 合作QQ: 532337155
  • 成都网站设计地址:成都市青羊区锣锅巷31号五金站写字楼6楼

小谭建站工作室

成都小谭网站建设公司拥有多年以上互联网从业经验的团队,始终保持务实的风格,以"帮助客户成功"为已任,专注于提供对客户有价值的服务。 我们已为众企业及上市公司提供专业的网站建设服务。我们不只是一家网站建设的网络公司;我们对营销、技术、管理都有自己独特见解,小谭建站采取“创意+综合+营销”一体化的方式为您提供更专业的服务!

小谭观点

相对传统的成都网站建设公司而言,小谭是互联网中的网站品牌策划,我们精于企业品牌与互联网相结合的整体战略服务。
我们始终认为,网站必须注入企业基因,真正使网站成为企业vi的一部分,让整个网站品牌策划体系变的深入而持久。