研究:右值引用,本质还是引用,一种权限比普通引用更大的引用?内部的实现可能是指针

struct DPoint3d
{
    //! x coordinate
    double x;
    //! y coordinate
    double y;
    //! z coordinate
    double z;

    int*p = nullptr;

    DPoint3d()
    {
        p = new int(5);
        std::cout << "DPoint3d" << endl;
    }
    DPoint3d(double _x, double _y, double _z)
    {
        p = new int(5);
        x = _x;
        y = _y;
        z = _z;
    }
    ~DPoint3d()
    {
        delete p;
        p = nullptr;

        std::cout << this << endl;

        std::cout << "~DPoint3d" << endl;
    }
    DPoint3d(const DPoint3d& pt)
    {
        std::cout << "123" << endl;
        x = pt.x;
        y = pt.y;
        z = pt.z;
    }

    DPoint3d(const DPoint3d&& pt)
    {
        std::cout << "456" << endl;
        x = pt.x;
        y = pt.y;
        z = pt.z;
    }

    DPoint3d& operator=(DPoint3d&& pt) {
        std::cout << "789" << endl;
        x = pt.x;
        y = pt.y;
        z = pt.z;
    }
};

考虑下面的函数,注意其返回值类型。

DPoint3d&& fun1()
{
    return DPoint3d{1,2,3}; //warning C4172: 返回局部变量或临时变量的地址。
注意“地址”这个词。说明DPoint3d&&代表了一种指针?
}

int main()
{
    {
        DPoint3d&& a = fun1();
        a.y = 20;
        std::cout << &a << endl;//一取地址,就露馅了
        int i = 0;
        ++i;
    }

a接到的对象已析构。错误用法。
所以,fun1就是返回了一个局部匿名对象的指针,但局部对象该析构还是析构。
所以,返回的就是指向这个局部匿名对象的指针,但返回的时候已经释放了,
所以指向了一块已经释放了的对象,自然会引发未定义行为。
DPoint3d&& fun1()
{
    return DPoint3d{1,2,3}; //warning C4172: 返回局部变量或临时变量的地址。
注意“地址”这个词。说明DPoint3d&&代表了一种指针?
}

int main()
{
    {
        DPoint3d a = fun1();
        a.y = 20;
        std::cout << &a << endl;//一取地址,就露馅了
        int i = 0;
        ++i;
    }

fun1在返回之前已经析构,但程序用析构的对象去初始化变量a,
但此时fun1里的那个析构的函数里的数据已经是无效值了。
a再去接这些数据,会导致未知行为。
错误用法。

所以,在函数的定义的角度,以下做法就是错误的:函数内部的局部变量,返回值类型是DPoint3d&&。不管提没提供移动构造函数。不管在外面是否用右值DPoint3d&&来接。

分析来看,右值引用&&,有点像一种指针,一种比引用&更牛逼一些的指针,因为引用只能引用左值,而右值引用可以直接引用右值,也可以通过std::move(左值)的方式进行引用。

为什么说右值引用像一种指针呢,看下面这个例子,编译器给出的警告信息内容:

int&& fun2()
{
    return 1; //注意这个提示: warning C4172: 返回局部变量或临时变量的地址。
注意“地址”这个词。说明DPoint3d&&代表了一种指针?
}

{
        int&& a = fun2();
        std::cout << &a << endl; //一取地址,a值就变了,随机值,比如3271
        int i = 0;
        ++i;
}


编译器提示:warning C4172: 返回局部变量或临时变量的地址
注意,说是返回了。。。的“地址”

返回值刚出来,可能编译器没刷新,还显示的是1:

 取地址后,a的值露出原形:

int&& fun2()
{
    return 1; // 默认构造
}
int main()
{
    {
        int a = fun2();
        std::cout << &a << endl;
        int i = 0;
        ++i;
    }

这个倒是对了,起码函数返回的瞬间把值给复制给了a。
毕竟是pod类型的数据。但尽量别这么用

看其他博客,都说右值可以延长变量的生命期,是不是这种情况,以右值是一种指针的方向理解一下:

DPoint3d fun1()
{
    return DPoint3d{1,2,3}; // 默认构造
}
    
{
    DPoint3d&& a = fun1();
    a.y = 20;
    std::cout << &a << endl;
    int i = 0;
    ++i;
}

这种情况下,不会调用到DPoint3d的任何构造函数(包括移动构造函数)

自始至终就是一个对象,如果打印对象地址,发现是一样的:
000000FA1D37DD78  构造的时候
000000FA1D37DD78  std::cout << &a << endl;的时候
000000FA1D37DD78  执行完++i;后析构的时候
~DPoint3d

DPoint3d fun1()
{
    return DPoint3d{1,2,3}; // 默认构造
}
    
{
    DPoint3d&& a = fun1();
    a.y = 20;
    std::cout << &a << endl;
    int i = 0;
    ++i;
}

这种情况下,不会调用到DPoint3d的任何构造函数(包括移动构造函数)
可能是进行了返回值优化

自始至终就是一个对象,如果打印对象地址,发现是一样的:
000000AB5B92DA78  构造的时候
000000AB5B92DA78  std::cout << &a << endl;的时候
000000AB5B92DA78  执行完++i;后析构的时候
~DPoint3d

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值