C++赋值和复制

C++赋值和复制

目录

一.说明

c++11 值的类型分为 左值, 纯右值,将亡值(return 返回). 将亡值属于右值. c++11 中引入了右值引用和移动语义,可以避免无谓的复制,提高了程序的性能,右值引用标记为T&&.(右值被使用的对象不再拥有变量的控制权(指针成员,不能再使用,已经移到到赋值对象),加快大对象的复制)

在模板中右值的特性:

  1. 左值和右值是独立于它们的类型,右值引用类型可能是左值也可能是右值

  2. auto&&或函数参数类型的自动推导的T&&是一个未定的引用类型, 它可能是左值引用,也可能是右值引用,取决于初始化的值类型

  3. 所有的右值引用叠加到右值引用上仍然是一个右值引用,其它引用叠加都为左值引用, (&& + && 输出 右值 , 其它情况 输出左值),当T&&为模版参数时,输入左值,它会变为左值引用, 输入右值则变为具名的右值引用 (模板的&&参数为虚化的右值,由实际输入决定)

  4. 编译器会将已命名的右值引用视为左值,而将未命名的右值视为右值

在非模板中

如果需要在非复制构造与赋值函数的参数为右值输入类型,需要显示声明(可以和左值参数 重载)

  • std::move:将左值变为右值, 前提参数没有const修饰(传入const修饰的变量依然为左值)
  • std::forward:完美转发模板输入类型(左值输入,输出左值 右值输入,输出右值)

二.例程

各种构造函数,右值使用,如下:

class mmt
{
public:
    template<typename T> //模板中: 带有&&的参数,是否为右值由输入参数类型最终决定!
    static void formatArgEnd(std::vector<WStfmt> &tab, T&& a) //a右值输入, 可接收左值和右值,
    {
        //tab.emplace_back(a);  调用左值算法
        tab.emplace_back(std::forward<T>(a)); //调用 左值或者右值算法,后面不能访问a (具体类型由a 输入决定)
        //emplace_back 支持左值或者右值类型   (如果a有右值构造函数执行右值构造,否则执行左值构造函数)
    }

     template<typename ...ARG>
     static WString formats(const char *pfmt, ARG&&... args) //模板 && args 可接收左值与右值 (非模板函数&&只能接收右值)
     {
         constexpr u32 argNum = sizeof ... (args); //可变参数的个数

         std::vector<WStfmt> tab;

         tab.reserve(argNum); //保留 argNum个数的空间
         (void)std::initializer_list<int> {(formatArgEnd(tab, std::forward<ARG>(args)), 0)...}; //std::forward 转发输入的args具体的类型(后面不能再访问args)

         //std::initializer_list 将可变参数展开(编译阶段已经展开)
         //如 formats("123", 1, 2, 3) 展开类型如下:(依次执行下列语句)
         // formatArgEnd(tab, 1)
         // formatArgEnd(tab, 2)
         // formatArgEnd(tab, 3)
         return WString::formatStfmt(pfmt, tab.data(), (s32)tab.size());
     }

};

class CPoint
{
public:
    ~CPoint() //析构函数
    {
        WDBG("析构");

        if(m_p != WNULL)
        {
            WDBG("释放内存");
            delete []m_p;
        }
    }

    CPoint() : m_x(0), m_y(0)
    {
        m_p = new char[100];

        WDBG("构造");
    }

    CPoint(s32 xIn) : m_x(xIn), m_y(0)
    {
        m_p = new char[100];
        WDBG("有参构造1");
    }

    CPoint(s32 xIn, s32 yIn) : m_x(xIn), m_y(yIn)
    {
        m_p = new char[100];
        WDBG("有参构造");
    }

    CPoint(const CPoint &p) : m_x(p.m_x), m_y(p.m_y)  //左值复制构造
    {
        m_p = new char[100];
        memcpy(m_p, p.m_p, 100);

        WDBG("复制构造");
    }

    CPoint(CPoint &&p) : m_x(p.m_x), m_y(p.m_y)  //右值复制构造 c++11才有 (如果内部有指针直接等号)
    {
        m_p = p.m_p; //不用复制内容,只修改指针指向
        p.m_p = WNULL;
        WDBG("复制构造 右值");
    }

    CPoint& operator=(const CPoint &p) //左值赋值
    {
        WDBG("赋值");
        m_x = p.m_x;
        m_y = p.m_y;

        memcpy(m_p, p.m_p, 100);
        return *this;
    }

    CPoint& operator=(CPoint &&p) //右值赋值 c++11才有
    {
        WDBG("赋值 右值");
        m_x = p.m_x;
        m_y = p.m_y;

        delete []m_p; //不用复制内容,只修改指针指向
        m_p = p.m_p;
        p.m_p = WNULL;
        return *this;
    }

    CPoint& operator+=(const CPoint &p) //其它和自己相加
    {
        WDBG("operator +=");
        m_x += p.m_x;
        m_y += p.m_y;
        return *this;
    }

    //判断相等
    friend  bool operator==(const CPoint &p1, const CPoint &p2)
    {
        WDBG("operator ==");
        return ((p1.m_x == p2.m_x) && (p1.m_y == p2.m_y));
    }

    //判断不相等
    friend  bool operator!=(const CPoint &p1, const CPoint &p2)
    {
        WDBG("operator !=");
        return !(p1 == p2);
    }

    //2个任意操作数相加
    friend CPoint operator+(const CPoint &p1, const CPoint &p2)
    {
        WDBG("operator+");
        return CPoint(p1.m_x + p2.m_x, p1.m_y + p2.m_y);
    }

    std::string toString() const;

    s32 m_x;
    s32 m_y;
    char *m_p;
};


std::string CPoint::toString() const
{
    return WString::formats("[%, %]", m_x, m_y);
}


struct Pset
{
public:
    Pset()
    {
    }

    ~Pset()
    {
    }

    void set(const CPoint &pt)
    {
        CPoint pm(pt);

        WDBG("set 左值");
    }

    void set(CPoint &&pt)
    {
        CPoint pm(pt); //参数进入函数后变为左值,如果需要为右值需显示移动

        WDBG("set 右值,pm左值");
    }

    void set2(CPoint &&pt)
    {
        CPoint pm(std::move(pt));

        WDBG("set 右值,pm右值");
    }
};

static void func2(CPoint &&p)
{
    WDBG("func in p1");

    CPoint p1(p); //调用左值

    WDBG("\r\n");
    WDBG("func in p2");

    CPoint p2(std::move(p));

    WDBG("\r\n");
    WDBG("func out");
}


static void test()
{
    WDBG("\r\n");
    {
        WDBG("\r\n");
        WDBG("p1");
        CPoint p1;

        WDBG("\r\n");
        WDBG("p2");
        CPoint p2(p1);

        WDBG("\r\n");
        WDBG("p3");
        CPoint p3;

        WDBG("\r\n");
        WDBG("p3=p2");
        p3 = p2;

        WDBG("\r\n");
        WDBG("p5(std::move(p3))");
        CPoint p5(std::move(p3)); //强制将p3 变为右值
        //后面 不允许访问p3

        WDBG("\r\n");
        WDBG("p6");
        CPoint p6;

        WDBG("\r\n");
        WDBG("p6 = std::move(p5)");
        p6 = std::move(p5);
        //后面 不允许访问p5
    }

    {
        WDBG("\r\n");
        WDBG("p10");
        CPoint p10;

        func2(std::move(p10));
    }

    {
        WDBG("\r\n");
        WDBG("30");

        CPoint p1;
        CPoint p2(1, 2);
        CPoint p3;

        WDBG("\r\n");
        WDBG("p1==p2");
        if(p1 == p2)
        {
            WDBG("P1 == P2");
        }
        else
        {
            WDBG("P1 != P2");
        }

        WDBG("\r\n");
        WDBG("p3 = p1 + p2");
        p3 = p1 + p2;

        WDBG("\r\n");
        WDBG("p3 = p1 + 1");
        p3 = p1 + 1;     //重要 如果参数能用指定的构造方法实现.既能实现调用!!!!

        WDBG("\r\n");
        WDBG("p3 = 2");
        p3 = 2;  //重要 如果参数能用指定的构造方法实现.既能实现调用!!!!
    }

    {
        WDBG("\r\n");
        WDBG("pa[3]");
        CPoint pa[3] = {CPoint(), CPoint(1, 2), CPoint(2, 3)}; //重要类数组初始化!!

        for(auto const &v : pa)
        {
            WDBG("res[%]", v);
        }
    }



    WDBG("\r\n");

    {
        WDBG("\r\n");
        WDBG("Pset");

        CPoint p(1, 2);
        CPoint p2(1, 2);

        Pset st;

        WDBG("\r\n");
        WDBG("Pset 左值");
        st.set(p);

        WDBG("\r\n");
        WDBG("Pset 右值, 内左");
        st.set(std::move(p));

        WDBG("\r\n");
        WDBG("Pset 右值,内右");
        st.set2(std::move(p2));

        //st.set2(p2); 编译不过,没有提供左值算法

    }

}

/*
[DBG] [2021-06-17 12:21:13.224] [main.cpp::test:249]  p1
[DBG] [2021-06-17 12:21:13.224] [main.cpp::CPoint:86]  构造
[DBG] [2021-06-17 12:21:13.224] [main.cpp::test:252]

[DBG] [2021-06-17 12:21:13.225] [main.cpp::test:253]  p2
[DBG] [2021-06-17 12:21:13.225] [main.cpp::CPoint:106]  复制构造
[DBG] [2021-06-17 12:21:13.225] [main.cpp::test:256]

[DBG] [2021-06-17 12:21:13.225] [main.cpp::test:257]  p3
[DBG] [2021-06-17 12:21:13.225] [main.cpp::CPoint:86]  构造
[DBG] [2021-06-17 12:21:13.225] [main.cpp::test:260]

[DBG] [2021-06-17 12:21:13.225] [main.cpp::test:261]  p3=p2
[DBG] [2021-06-17 12:21:13.226] [main.cpp::operator=:118]  赋值
[DBG] [2021-06-17 12:21:13.226] [main.cpp::test:264]

[DBG] [2021-06-17 12:21:13.226] [main.cpp::test:265]  p5(std::move(p3))
[DBG] [2021-06-17 12:21:13.226] [main.cpp::CPoint:113]  复制构造 右值
[DBG] [2021-06-17 12:21:13.226] [main.cpp::test:269]

[DBG] [2021-06-17 12:21:13.226] [main.cpp::test:270]  p6
[DBG] [2021-06-17 12:21:13.227] [main.cpp::CPoint:86]  构造
[DBG] [2021-06-17 12:21:13.227] [main.cpp::test:273]

[DBG] [2021-06-17 12:21:13.227] [main.cpp::test:274]  p6 = std::move(p5)
[DBG] [2021-06-17 12:21:13.227] [main.cpp::operator=:128]  赋值 右值
[DBG] [2021-06-17 12:21:13.227] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.227] [main.cpp::~CPoint:77]  释放内存
[DBG] [2021-06-17 12:21:13.227] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.227] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.227] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.227] [main.cpp::~CPoint:77]  释放内存
[DBG] [2021-06-17 12:21:13.228] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.228] [main.cpp::~CPoint:77]  释放内存
[DBG] [2021-06-17 12:21:13.228] [main.cpp::test:280]

[DBG] [2021-06-17 12:21:13.228] [main.cpp::test:281]  p10
[DBG] [2021-06-17 12:21:13.228] [main.cpp::CPoint:86]  构造
[DBG] [2021-06-17 12:21:13.228] [main.cpp::func2:226]  func in p1
[DBG] [2021-06-17 12:21:13.228] [main.cpp::CPoint:106]  复制构造
[DBG] [2021-06-17 12:21:13.228] [main.cpp::func2:230]

[DBG] [2021-06-17 12:21:13.228] [main.cpp::func2:231]  func in p2
[DBG] [2021-06-17 12:21:13.228] [main.cpp::CPoint:113]  复制构造 右值
[DBG] [2021-06-17 12:21:13.228] [main.cpp::func2:235]

[DBG] [2021-06-17 12:21:13.228] [main.cpp::func2:236]  func out
[DBG] [2021-06-17 12:21:13.228] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.228] [main.cpp::~CPoint:77]  释放内存
[DBG] [2021-06-17 12:21:13.228] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.229] [main.cpp::~CPoint:77]  释放内存
[DBG] [2021-06-17 12:21:13.229] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.229] [main.cpp::test:288]

[DBG] [2021-06-17 12:21:13.229] [main.cpp::test:289]  30
[DBG] [2021-06-17 12:21:13.229] [main.cpp::CPoint:86]  构造
[DBG] [2021-06-17 12:21:13.229] [main.cpp::CPoint:98]  有参构造
[DBG] [2021-06-17 12:21:13.229] [main.cpp::CPoint:86]  构造
[DBG] [2021-06-17 12:21:13.229] [main.cpp::test:295]

[DBG] [2021-06-17 12:21:13.229] [main.cpp::test:296]  p1==p2
[DBG] [2021-06-17 12:21:13.229] [main.cpp::operator==:149]  operator ==
[DBG] [2021-06-17 12:21:13.229] [main.cpp::test:303]  P1 != P2
[DBG] [2021-06-17 12:21:13.229] [main.cpp::test:306]

[DBG] [2021-06-17 12:21:13.229] [main.cpp::test:307]  p3 = p1 + p2
[DBG] [2021-06-17 12:21:13.229] [main.cpp::operator+:163]  operator+
[DBG] [2021-06-17 12:21:13.229] [main.cpp::CPoint:98]  有参构造
[DBG] [2021-06-17 12:21:13.229] [main.cpp::operator=:128]  赋值 右值
[DBG] [2021-06-17 12:21:13.229] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.229] [main.cpp::test:310]

[DBG] [2021-06-17 12:21:13.229] [main.cpp::test:311]  p3 = p1 + 1
[DBG] [2021-06-17 12:21:13.229] [main.cpp::CPoint:92]  有参构造1
[DBG] [2021-06-17 12:21:13.229] [main.cpp::operator+:163]  operator+
[DBG] [2021-06-17 12:21:13.229] [main.cpp::CPoint:98]  有参构造
[DBG] [2021-06-17 12:21:13.229] [main.cpp::operator=:128]  赋值 右值
[DBG] [2021-06-17 12:21:13.229] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.229] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.229] [main.cpp::~CPoint:77]  释放内存
[DBG] [2021-06-17 12:21:13.229] [main.cpp::test:314]

[DBG] [2021-06-17 12:21:13.229] [main.cpp::test:315]  p3 = 2
[DBG] [2021-06-17 12:21:13.229] [main.cpp::CPoint:92]  有参构造1
[DBG] [2021-06-17 12:21:13.229] [main.cpp::operator=:128]  赋值 右值
[DBG] [2021-06-17 12:21:13.229] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.229] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:77]  释放内存
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:77]  释放内存
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:77]  释放内存
[DBG] [2021-06-17 12:21:13.230] [main.cpp::test:320]

[DBG] [2021-06-17 12:21:13.230] [main.cpp::test:321]  pa[3]
[DBG] [2021-06-17 12:21:13.230] [main.cpp::CPoint:86]  构造
[DBG] [2021-06-17 12:21:13.230] [main.cpp::CPoint:98]  有参构造
[DBG] [2021-06-17 12:21:13.230] [main.cpp::CPoint:98]  有参构造
[DBG] [2021-06-17 12:21:13.230] [main.cpp::test:326]  res[[0, 0]]
[DBG] [2021-06-17 12:21:13.230] [main.cpp::test:326]  res[[1, 2]]
[DBG] [2021-06-17 12:21:13.230] [main.cpp::test:326]  res[[2, 3]]
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:77]  释放内存
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:77]  释放内存
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:77]  释放内存
[DBG] [2021-06-17 12:21:13.230] [main.cpp::test:332]

[DBG] [2021-06-17 12:21:13.230] [main.cpp::test:335]

[DBG] [2021-06-17 12:21:13.230] [main.cpp::test:336]  Pset
[DBG] [2021-06-17 12:21:13.230] [main.cpp::CPoint:98]  有参构造
[DBG] [2021-06-17 12:21:13.230] [main.cpp::CPoint:98]  有参构造
[DBG] [2021-06-17 12:21:13.230] [main.cpp::test:343]

[DBG] [2021-06-17 12:21:13.230] [main.cpp::test:344]  Pset 左值
[DBG] [2021-06-17 12:21:13.230] [main.cpp::CPoint:106]  复制构造
[DBG] [2021-06-17 12:21:13.230] [main.cpp::set:196]  set 左值
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:77]  释放内存
[DBG] [2021-06-17 12:21:13.230] [main.cpp::test:347]

[DBG] [2021-06-17 12:21:13.230] [main.cpp::test:348]  Pset 右值, 内左
[DBG] [2021-06-17 12:21:13.230] [main.cpp::CPoint:106]  复制构造
[DBG] [2021-06-17 12:21:13.230] [main.cpp::set:203]  set 右值,pm左值
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:77]  释放内存
[DBG] [2021-06-17 12:21:13.230] [main.cpp::test:351]

[DBG] [2021-06-17 12:21:13.230] [main.cpp::test:352]  Pset 右值,内右
[DBG] [2021-06-17 12:21:13.230] [main.cpp::CPoint:113]  复制构造 右值
[DBG] [2021-06-17 12:21:13.230] [main.cpp::set2:210]  set 右值,pm右值
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:77]  释放内存
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:73]  析构
[DBG] [2021-06-17 12:21:13.230] [main.cpp::~CPoint:77]  释放内存
*/