C++右值引用问题解决

 更新时间:2023年06月20日 08:39:19   作者:dyyfyyds_  
本文主要介绍了C++右值引用问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

1、右值引用与函数重载

class Int
{
    int value;
public:
    Int(int x = 0) :value(x) { cout << "create " << this << endl; }
    ~Int() { cout << "destroy " << this << endl; }
    Int(const Int& it) :value(it.value)
    {
        cout << &it << " copy " << this << endl;
    }
    Int& operator=(const Int& it)
    {
        if (this != &it)
        {
            value = it.value;
        }
        cout << &it << " operator= " << this << endl;
        return *this;
    }
    void PrintValue()const
    {
        cout<<"value: "<<value<<endl;
    }
    Int& SetValue(int x)
    {
        value=x;
        return *this;
    }
};
//void func(Int a){}//会和void fun(Int&& c)形成二义性,原因是无名对象即可以赋值给对象也可以赋值给右值引用
void func(Int& a)
{
    cout<<"lvalue_reference"<<endl;
}
void func(const Int& b)
{
    cout<<"lvalue_const_reference"<<endl;
}
void func(Int&& c)
{
    cout<<"rvalue_reference"<<endl;
}
int main()
{
    Int x(10);
    const Int y(20);
    func(x);//优先匹配func(Int& a),没有就匹配func(const Int& b)
    func(y);//只能匹配func(const Int& b)
    func(Int(30));//优先匹配func(Int&& c),没有就匹配func(const Int& b)
    Int z(10);
    func((Int&&)z);//调用func(Int&& c)
    func((const Int&&)z);//调用func(const Int& b)
    return 0;
}

2、右值引用优化性能,避免深拷贝

class MyString
{
private:
    char* str;
public:
    MyString(const char* p=nullptr):str(nullptr)
    {
        if(p!=nullptr)
        {
            int n=strlen(p)+1;
            str=new char[n];
            strcpy_s(str,n,p);
        }
        cout<<"Create MyString"<<this<<endl;
    }
    ~MyString()
    {
        if(str!=nullptr)
        {
            delete[] str;
            str=nullptr;
        }
        str=nullptr;
    }
    MyString(const MyString& st):str(nullptr)//深拷贝
    {
        if(st.str!=nullptr)
        {
            int n=strlen(st.str)+1;
            str=new char[n];
            strcpy_s(str,n,st.str);
        }
        cout<<"Copy Create MyString"<<this<<endl;
    }
    MyString& operator=(const MyString& st)//深赋值
    {
        if(this==&st||str==st.str)
        {
            return *this;
        }
        delete[] str;//str为空时也可以进行delete,因为delete调用free,在free中会进行判空
        int n=strlen(st.str)+1;
        str=new char[n];
        strcpy_s(str,n,st.str);
        cout<<this<<"operator= MyString"<<&st<<endl;
        return *this;
    }
    void Print()const
    {
        if(str!=nullptr)
        {
            cout<<str<<endl;
        }
    }
};

2.1使用深拷贝和深复制会对堆区空间造成巨大影响

MyString func(const char* p)
{
    MyString tmp(p);
    return tmp;
}
int main()
{
    MyString s1("hello");
    s1=func("helloworld");
    s1.Print();
    return 0;
}

在main函数中能看见的字符串"hello"和"helloworld"均存放在.data区,p指针指向的也是.data区的"helloworld"。运行时进入主函数,首先创建s1对象,在堆区空间申请空间存放字符串"hello",s1.str指向该堆区空间。

再调用func()函数,进入func()函数先创建tmp对象,在堆区申请空间存放字符串"helloworld",tmp.str指向该堆区空间。

返回时,使用tmp对象构建将亡值对象xvalue,同样的,在堆区申请和tmp所申请大小相同的空间,将字符串"helloworld"赋值过去进行存放,xvalue.str指向该堆区空间。需要注意的是xvalue这个将亡值对象是在main栈帧中创建的,而不是func()函数栈帧中。创建完将亡值对象后,func()函数结束销毁tmp对象,将其所指向的堆区空间进行释放。

再回到主函数,将该将亡值对象赋值给s1对象时,调用赋值函数。首先申请和将亡值对象申请大小相同的空间,将字符串"helloworld"赋值过去进行存放,释放s1对象开始指向的堆区空间,之后再将s1.str重新指向新申请的空间。析构所有对象这样整个程序结束。

由此看来,深拷贝在一些情况下严重干扰堆空间,对其不停的申请和释放。

2.2使用移动拷贝构造和移动赋值提升性能(移动资源)

//移动拷贝构造
MyString(MyString&& st):str(nullptr)
{
    str=st.str;
    st.str=nullptr;
    cout<<"Move Copy Create"<<endl;
}
//移动赋值
MyString& operator=(MyString&& st)
{
    if(this==&st)return *this;
    delete[] str;
    str=st.str;
    st.str=nullptr;
    cout<<"Move Operator= "<<endl;
    return *this;
}

加入移动拷贝构造和移动赋值后再执行下面代码:

MyString func(const char* p)
{
    MyString tmp(p);
    return tmp;
}
int main()
{
    MyString s1("hello");
    s1=func("helloworld");
    s1.Print();
    return 0;
}

同样的,运行时进入主函数,首先创建s1对象,在堆区空间申请空间存放字符串"hello",s1.str指向该堆区空间。

再调用func()函数,进入func()函数先创建tmp对象,在堆区申请空间存放字符串"helloworld",tmp.str指向该堆区空间。

返回时tmp为左值对象,但系统认为在func函数中定义的局部对象tmp其生存期只在该函数中,使用return返回tmp对象时,认为是要将tmp的资源进行移动,就会将其看成是将亡值。(系统“作弊”)

所以在func函数返回前会调用移动拷贝构造函数将tmp对象的资源移动给创建的xvalue将亡值,func函数结束,析构tmp对象。

回到main函数时,将将亡值xvalue赋值给s1对象时,调用移动赋值函数,将xvalue的资源再次移动给s1对象,赋值结束后xvalue将亡值对象消亡,打印s1的内容,析构所有对象程序结束。

在这个过程中,并没有多次反复的申请空间和释放空间,而是将申请的空间在多个对象之间进行移动,这样就使得程序的性能提高。

如果将str设置为公有,即可在func和主函数中打印str的值,会发现tmp和func("helloworld")以及赋值完成后的s1的str值都相同,说明他们一直指向同一块内存空间。

到此这篇关于C++右值引用问题的文章就介绍到这了,更多相关C++右值引用问题内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++模板类的用法实例

    C++模板类的用法实例

    这篇文章主要介绍了C++模板类的用法实例,以实例形式详细讲述了模板类的接口、成员、内联函数等概念及用法,需要的朋友可以参考下
    2014-10-10
  • C++ Primer的变量和基本类型详解

    C++ Primer的变量和基本类型详解

    这篇文章主要为大家介绍了C++ Primer,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-01-01
  • 华为笔试算法题汇总

    华为笔试算法题汇总

    这篇文章主要为大家汇总了华为笔试算法题,每一题都给出了详细代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-09-09
  • C++选择排序算法实例

    C++选择排序算法实例

    这篇文章主要介绍了C++选择排序算法实例,本文先是介绍了什么是选择排序,然后给出了实现代码,需要的朋友可以参考下
    2014-10-10
  • C语言库函数qsort及bsearch快速排序算法使用解析

    C语言库函数qsort及bsearch快速排序算法使用解析

    这篇文章主要为大家介绍了C语言库函数qsort及bsearch快速排序算法的使用示例解析
    2022-02-02
  • 一盘王者的时间用C语言实现三子棋

    一盘王者的时间用C语言实现三子棋

    相信我们都玩过三子棋,规则很简单,但想用c语言做出这个游戏,事实上也是比较简单的,下面通过c语言进行对五子棋的分析
    2022-02-02
  • C++ Qt开发之使用QNetworkAccessManager实现Web网页访问

    C++ Qt开发之使用QNetworkAccessManager实现Web网页访问

    Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,本文主要介绍了如何运用QNetworkAccessManager组件实现Web网页访问,需要的可以参考下
    2024-03-03
  • C++中std::sort函数介绍和使用场景

    C++中std::sort函数介绍和使用场景

    std::sort函数是C++标准库中常用的排序函数之一,它可以对各种类型的序列进行排序,本文就来介绍一下C++中std::sort函数介绍和使用场景,感兴趣的可以了解一下
    2024-02-02
  • C++ std::bind用法详解

    C++ std::bind用法详解

    这篇文章主要介绍了C++ std::bind用法详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-09-09
  • C++中set/multiset与map/multimap的使用详解

    C++中set/multiset与map/multimap的使用详解

    这篇文章主要为大家详细介绍了C++中set/multiset与map/multimap的使用,文中的示例代码讲解详细,具有一定的借鉴价值,需要的可以参考一下
    2023-02-02

最新评论