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++使用初始化列表的方式来初始化字段的方法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • 清除3389远程登录日志

    清除3389远程登录日志

    这篇文章主要介绍了清除3389远程登录日志示例,需要的朋友可以参考下
    2014-01-01
  • linux根据pid获取进程名和获取进程pid(c语言获取pid)

    linux根据pid获取进程名和获取进程pid(c语言获取pid)

    status文件,第一行的Name即为进程名,C程序实现根据PID获取进程名和根据进程名获取PID,大家参考使用吧
    2013-12-12
  • C++实现图书管理系统(文件操作与类)

    C++实现图书管理系统(文件操作与类)

    这篇文章主要为大家详细介绍了C++实现图书管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • C++ Boost weak_ptr智能指针超详细讲解

    C++ Boost weak_ptr智能指针超详细讲解

    智能指针是一种像指针的C++对象,但它能够在对象不使用的时候自己销毁掉。虽然STL提供了auto_ptr,但是由于不能同容器一起使用(不支持拷贝和赋值操作),因此很少有人使用。它是Boost各组件中,应用最为广泛的一个
    2022-11-11
  • C++指向类成员的指针详解

    C++指向类成员的指针详解

    指向类成员的指针总的来讲可以分为两大类四小类(指向数据成员还是成员函数,指向普通成员还是静态成员),希望本片文章能给你带来帮助
    2021-09-09
  • Qt编写地图之实现跨平台功能

    Qt编写地图之实现跨平台功能

    这篇文章主要介绍了如何利用Qt编写地图应用时实现跨平台功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2022-02-02
  • C++下如何将TensorFlow模型封装成DLL供C#调用

    C++下如何将TensorFlow模型封装成DLL供C#调用

    这篇文章主要介绍了C++下如何将TensorFlow模型封装成DLL供C#调用问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • C++制作DLL文件的方法详解

    C++制作DLL文件的方法详解

    本文主要介绍如何制作DLL,将代码类中的方法以接口的形式暴露出来给exe程序使用。会涉及类厂创建方法实例、声明DLL接口、.def文件的使用等,感兴趣的可以了解一下
    2023-04-04
  • C语言简单实现门禁系统

    C语言简单实现门禁系统

    这篇文章主要为大家详细介绍了C语言简单实现门禁系统,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-01-01

最新评论