C——右值引用是什么意思?
- 内容介绍
- 相关推荐
本文共计1346个文字,预计阅读时间需要6分钟。
1. 绑定到一个将要销毁的右值的引用——移动
2.左值表达式——对象的身份;右值表达式——对象的值
int i=42; int r=i; int rr=i; // x, int r2=i * 42; // x, 左值不能绑定到右值引用1.绑定到一个将要销毁的右值的引用——移动
2.左值表达式——对象的身份;右值表达式——对象的值
int i=42;
int& r=i;
int&& rr=i;//×,左值不能绑定到右值引用
int& r2=i*42;//×,右值不能绑定到左值引用
const int& r3=i*42;//√,右值可以绑定const引用
int&& rr2 = i*42;//右值绑定到右值引用
int&& rr1=42;//√
int&& rr2=rr1;//×
int&& rr3=std::move(rr1);//√,使用std可以避免潜在的命名冲突
3.左值持久,右值短暂(一般是字面常量,或表达式创建的临时对象(如上))//变量寿命很长的,出}才会被销毁
4.右值引用的对象即将被销毁且该对象没有其他用户
5.移动构造函数和移动赋值运算符
5.1 移动构造函数不分配任何新内存,直接接管给定的内存,接管后将原对象置为null
StrVec(StrVec&& s) noexcept :elements(s.elements), first_free(s.first_free), cap(s.cap)//接管s的资源 { s.elements = s.first_free = s.cap = nullptr;//让s进入null,否则销毁s时,构造也会被销毁 }
5.2 移动赋值运算符
StrVec& operator=(StrVec&& rhs) noexcept//为何其他地方不需要? { if (this != &rhs)//检测自赋值 { free(); elements = rhs.elements; first_free = rhs.first_free; cap = rhs.cap; rhs.elements = rhs.first_free = rhs.cap = nullptr; } return *this; }
5.3 合成移动操作:
类定义了自己的拷贝构造函数、拷贝赋值运算符或析构函数,便不会合成移动构造函数和移动赋值运算符。只有当一个类未定义任何自己版本的拷贝控制成员,且类的每个非static数据成员都可以移动时,编译器才会为其合成移动构造函数或移动赋值运算符。
struct X{ int i; //内置类型,可以移动 string s;//string有定义的移动操作 }; struct hasX{ X men;//X有合成的移动操作 }; X x,x2=std::move(x);//使用合成移动构造函数 hasX hx,hx2=std::move(hx); //使用合成移动构造函数
5.4 定义了一个移动构造函数或移动赋值运算符的类,必须定义自己的拷贝操作。否则,这些成员将是delete的
6.移动右值,拷贝左值。但若没有移动构造函数,右值也被拷贝。
7.一个类有一个可用的拷贝构造函数,没有移动构造函数,则对象是通过拷贝构造函数来“移动”的。同理,适用于拷贝赋值运算符和移动赋值运算符。
class StrVec {
public:
StrVec() :elements(nullptr), first_free(nullptr), cap(nullptr) {}//默认初始化
StrVec(const StrVec&);//拷贝构造
StrVec& operator=(const StrVec&);//拷贝赋值运算符
~StrVec();
StrVec(StrVec&& s) noexcept :elements(s.elements), first_free(s.first_free), cap(s.cap)//接管s的资源
{
s.elements = s.first_free = s.cap = nullptr;//让s进入null
}
StrVec& operator=(StrVec&& rhs) noexcept
{
if (this != &rhs)//检测自赋值
{
free();
elements = rhs.elements;
first_free = rhs.first_free;
cap = rhs.cap;
rhs.elements = rhs.first_free = rhs.cap = nullptr;
}
return *this;
}
void push_back(const string&);//拷贝元素
void push_back(string&&);//移动元素,右值引用
string* begin() const { return elements; }
string* end() const { return first_free; }
size_t size() const { return first_free - elements; }
size_t capacity() const { return cap - elements; }
private:
static std::allocator<std::string> alloc;//分配器
void chk_n_alloc() { if (size() == capacity()) reallocate(); }//每一次添加元素都要check
pair<string*, string*> alloc_n_copy(const string*, const string*);//工具,给拷贝构造、赋值运算符和析构用
void reallocate(); //获得更多内存并拷贝元素
void free(); //销毁释放内存
std::string *elements; //指向数组元素的指针
std::string *first_free; //指向第一个空闲元素的指针
std::string *cap; //指向数组尾后位置的指针
};
void StrVec::push_back(const string& s)
{
chk_n_alloc();//确保空间够容纳新的元素
alloc.construct(first_free++, s);
}
void StrVec::push_back(string&& s)
{
chk_n_alloc();//确保空间够容纳新的元素
alloc.construct(first_free++, std::move(s));
}
StrVec vec;
string s = "some thing";
vec.push_back(s);//调用push_back(const string& s)
vec.push_back("done");//调用push_back(string&& s)
8.左值和右值的引用成员函数
8.1 右值引用如何修饰成员函数,什么含义,怎么调用
class Foo { Foo& operator=(const Foo& rhs) & { //rhs赋值给本对象 return *this;//指向左值 } Foo sorted() && ; Foo sorted() const &; private: list<int> data; }; Foo Foo::sorted() &&//本对象是右值,没有其他用户,可以对源对象排序 { sort(data.begin(), data.end()); return *this; } Foo Foo::sorted() const &//本对象是左值,只能先复制一份,再排序,不能改变原来的 { Foo ret(*this); sort(ret.data.begin(), ret.data.end()); return ret; }
8.2 定义两个或以上同名同参数列表的成员函数,要么都加上引用限定符,要么都不加
Foo sorted() && ;//1 Foo sorted() const &;//2都加(如果没有&就×) using Comp = bool(const int&, const int&);//com是函数类型的类型别名 Foo sorted(Comp*);//3 Foo sorted(Comp*) const;//4都不加(如果有&就×)
本文共计1346个文字,预计阅读时间需要6分钟。
1. 绑定到一个将要销毁的右值的引用——移动
2.左值表达式——对象的身份;右值表达式——对象的值
int i=42; int r=i; int rr=i; // x, int r2=i * 42; // x, 左值不能绑定到右值引用1.绑定到一个将要销毁的右值的引用——移动
2.左值表达式——对象的身份;右值表达式——对象的值
int i=42;
int& r=i;
int&& rr=i;//×,左值不能绑定到右值引用
int& r2=i*42;//×,右值不能绑定到左值引用
const int& r3=i*42;//√,右值可以绑定const引用
int&& rr2 = i*42;//右值绑定到右值引用
int&& rr1=42;//√
int&& rr2=rr1;//×
int&& rr3=std::move(rr1);//√,使用std可以避免潜在的命名冲突
3.左值持久,右值短暂(一般是字面常量,或表达式创建的临时对象(如上))//变量寿命很长的,出}才会被销毁
4.右值引用的对象即将被销毁且该对象没有其他用户
5.移动构造函数和移动赋值运算符
5.1 移动构造函数不分配任何新内存,直接接管给定的内存,接管后将原对象置为null
StrVec(StrVec&& s) noexcept :elements(s.elements), first_free(s.first_free), cap(s.cap)//接管s的资源 { s.elements = s.first_free = s.cap = nullptr;//让s进入null,否则销毁s时,构造也会被销毁 }
5.2 移动赋值运算符
StrVec& operator=(StrVec&& rhs) noexcept//为何其他地方不需要? { if (this != &rhs)//检测自赋值 { free(); elements = rhs.elements; first_free = rhs.first_free; cap = rhs.cap; rhs.elements = rhs.first_free = rhs.cap = nullptr; } return *this; }
5.3 合成移动操作:
类定义了自己的拷贝构造函数、拷贝赋值运算符或析构函数,便不会合成移动构造函数和移动赋值运算符。只有当一个类未定义任何自己版本的拷贝控制成员,且类的每个非static数据成员都可以移动时,编译器才会为其合成移动构造函数或移动赋值运算符。
struct X{ int i; //内置类型,可以移动 string s;//string有定义的移动操作 }; struct hasX{ X men;//X有合成的移动操作 }; X x,x2=std::move(x);//使用合成移动构造函数 hasX hx,hx2=std::move(hx); //使用合成移动构造函数
5.4 定义了一个移动构造函数或移动赋值运算符的类,必须定义自己的拷贝操作。否则,这些成员将是delete的
6.移动右值,拷贝左值。但若没有移动构造函数,右值也被拷贝。
7.一个类有一个可用的拷贝构造函数,没有移动构造函数,则对象是通过拷贝构造函数来“移动”的。同理,适用于拷贝赋值运算符和移动赋值运算符。
class StrVec {
public:
StrVec() :elements(nullptr), first_free(nullptr), cap(nullptr) {}//默认初始化
StrVec(const StrVec&);//拷贝构造
StrVec& operator=(const StrVec&);//拷贝赋值运算符
~StrVec();
StrVec(StrVec&& s) noexcept :elements(s.elements), first_free(s.first_free), cap(s.cap)//接管s的资源
{
s.elements = s.first_free = s.cap = nullptr;//让s进入null
}
StrVec& operator=(StrVec&& rhs) noexcept
{
if (this != &rhs)//检测自赋值
{
free();
elements = rhs.elements;
first_free = rhs.first_free;
cap = rhs.cap;
rhs.elements = rhs.first_free = rhs.cap = nullptr;
}
return *this;
}
void push_back(const string&);//拷贝元素
void push_back(string&&);//移动元素,右值引用
string* begin() const { return elements; }
string* end() const { return first_free; }
size_t size() const { return first_free - elements; }
size_t capacity() const { return cap - elements; }
private:
static std::allocator<std::string> alloc;//分配器
void chk_n_alloc() { if (size() == capacity()) reallocate(); }//每一次添加元素都要check
pair<string*, string*> alloc_n_copy(const string*, const string*);//工具,给拷贝构造、赋值运算符和析构用
void reallocate(); //获得更多内存并拷贝元素
void free(); //销毁释放内存
std::string *elements; //指向数组元素的指针
std::string *first_free; //指向第一个空闲元素的指针
std::string *cap; //指向数组尾后位置的指针
};
void StrVec::push_back(const string& s)
{
chk_n_alloc();//确保空间够容纳新的元素
alloc.construct(first_free++, s);
}
void StrVec::push_back(string&& s)
{
chk_n_alloc();//确保空间够容纳新的元素
alloc.construct(first_free++, std::move(s));
}
StrVec vec;
string s = "some thing";
vec.push_back(s);//调用push_back(const string& s)
vec.push_back("done");//调用push_back(string&& s)
8.左值和右值的引用成员函数
8.1 右值引用如何修饰成员函数,什么含义,怎么调用
class Foo { Foo& operator=(const Foo& rhs) & { //rhs赋值给本对象 return *this;//指向左值 } Foo sorted() && ; Foo sorted() const &; private: list<int> data; }; Foo Foo::sorted() &&//本对象是右值,没有其他用户,可以对源对象排序 { sort(data.begin(), data.end()); return *this; } Foo Foo::sorted() const &//本对象是左值,只能先复制一份,再排序,不能改变原来的 { Foo ret(*this); sort(ret.data.begin(), ret.data.end()); return ret; }
8.2 定义两个或以上同名同参数列表的成员函数,要么都加上引用限定符,要么都不加
Foo sorted() && ;//1 Foo sorted() const &;//2都加(如果没有&就×) using Comp = bool(const int&, const int&);//com是函数类型的类型别名 Foo sorted(Comp*);//3 Foo sorted(Comp*) const;//4都不加(如果有&就×)

