目录
1. 引言
C++中
&
有三种用途,而&&
有两种用途
2. &的作用
2.1 位运算
C++中的位运算十分高效,数据分段时经常用到!
例如,统计一个数字中有多少位是1的个数,代码如下:
int count(int x) {
int res = 0;
while(x) {
if (x & 1)
res++;
x = x >> 1;
}
return res;
}
获取n的第k位数字:
n >> k & 1;
返回n的最后一位1:
lowbit(n) = n & -n;
2.2 取地址
&
可以用于获取函数、变量地址等;
int a = 10;
int *pa = &a; // pa指针指向a的地址
void isEqual(int a, int b){
return a == b;
}
void (*func)(int, int);
func pfun = &isEqual;// 声明和初始化函数指针地址
2.3 引用
常用于函数传参,临时变量引用等
避免引起拷贝
std::vector<std::vector<int> > vec(10);
auto &pv = vec[0];// 引用数组的值,避免复制
3. &&的用途
3.1 逻辑运算符AND
用于条件判断
3.2 右值引用
C++11之后才有的效果,即移动语义。
3.2.1 背景知识
若函数的返回值是一个对象的话,则可能需要对函数中的局部对象进行拷贝。若对象很大,则会影响程序的效率。那么如何避免对象返回的拷贝呢?
拷贝构造函数的执行时机:
- 使用一个对象给另一个对象进行初始化;
- 对象为函数返回值,以值的方式返回;
- 对象作为函数参数,以值传递方式传递给函数;
#include <iostream>
using namespace std;
/*
注意点:
有些编译器出于程序执行效率的考虑,编译的时候进行了优化,
函数返回值对象就不用复制构造函数初始化了,但这并不符合 C++ 的标准
*/
class A {
public:
A();
A(char _c): c(_c) {cout << "构造函数执行" << endl;}
A(const A& rhs){
cout << " 拷贝构造函数执行" << endl;
c = rhs.c;
}
virtual ~A() {
cout << " 析构函数执行" << endl;
}
A clone() const{
A a = *this; // 拷贝构造函数
return a;// 析构函数,拷贝构造函数
}
private:
char c;
};
int main() {
A a = A('b');// 构造函数,=
a.clone();
return 0;
}
程序输出结果:
# g++ 10.0+
构造函数执行
拷贝构造函数执行
析构函数执行
析构函数执行
# vc++
构造函数执行
拷贝构造函数执行
拷贝构造函数执行
析构函数执行
析构函数执行
有些编译器出于程序执行效率的考虑,编译的时候进行了优化,函数返回值对象就不用复制构造函数初始化了,但这并不符合 C++ 的标准
拷贝构造函数和赋值运算符的联系和区别:
- 两者都可以将一个对象的值复制给另外一个对象;
- 拷贝构造函数使用传入的对象生成一个新对象;而赋值运算则只是将值复制给另外一个对象,没有新对象的产生;
- 判断依据:看是否有新的对象生成;
3.2.2 左值和右值
C++如何判断左值还是右值:
-
左值一般是可寻址的变量,右值一般不可寻址
左值即我们之前使用那些变量,而右值则是一些常量或无名临时对象
-
左值有持久性,右值有短暂性
-
左值符号
&
,右值符号&&
3.2.3 移动构造函数和移动赋值函数
移动和拷贝是相对的,移动类似于文件移动,从一个位置移到另外一个位置,拷贝则是数据的赋值,类似于文件复制;
实现对象复制可以使用拷贝构造函数和赋值运算符;
实现转移语义,则可以定义转移构造函数或转移赋值操作符;
对于右值的拷贝和赋值会调用转移构造函数和转移操作符,若转移构造函数和转移操作符,则拷贝函数或赋值运算将会被调用;
移动构造函数的构建:
对于类A,定义如下:
class A{
public:
explicit A(size_t len): _length(len), _data(new int[len]) {
std::cout << "构造函数" << std::endl;
}
~A() {
std::cout << "析构函数" << std::endl;
}
A(const A& other): _length(other._length), _data(new int[other._length]) {
std::cout << "拷贝构造函数" << std::endl;
std::copy(other._data, other._data + _length, _data);
}
// 赋值运算
A & operator=(const A& other) {
std::cout << "赋值运算=" << std::endl;
if (this != &other) {
delete[ ]_data;
_length = other._length;
_data = new int[_length];
std::copy(other._data, other._data + _length, _data);
}
return *this;
}
int length() const {
return _length;
}
private:
size_t _length;
int *_data;
};
为上述的类添加移动构造函数:
- 定义一个空的构造函数方法,参数为一个对类类型的右值引用
- 在移动构造函数中,将源对象的类数据成员添加到构造对象中;
- 将源对象的数据成员恢复默认值,防止析构函数多次释放资源;
// 1 空的构造函数
A(const A&& rhs) : _length(0), _data(nullptr) {
// 2 数据成员赋值
_data = rhs._data;
_length= rhs._length;
// 3 设置默认值,防止多次析构
rhs._length = 0;
rhs._data = nullptr;
}
添加移动赋值运算:
- 定义一个空的赋值运算符,该运算符的参数为一个右值引用
- 避免将对象赋值给自身;
- 条件语句中,要将其赋值的对象中释放所有内存,然后执行移动构造函数中的步骤2和3;
- 返回当前对象的引用;
// 1
A & operator=(const A&& rhs) {
if (*this != rhs) {
delete[] _data;// 2
_data = rhs._data, _length = rhs._length; // 3
rhs._data = nullptr, rhs._length = 0; // 3
}
return *this;//4
}
注意:
如果为你的类同时提供了移动构造函数和移动赋值运算符,则可以编写移动构造函数来调用移动赋值运算符,从而消除冗余代码。
// 移动构造函数
A(A&& other) noexcept
: _data(nullptr)
, _length(0)
{
*this = std::move(other);
}
std::move() 将左值转换为右值
标签:右值,对象,rhs,笔记,C++,&&,拷贝,构造函数,赋值 From: https://www.cnblogs.com/zuixime0515/p/17244022.html