1.为什么重载运算符要返回自身的引用
//声明了重载运算符=函数。default是告诉编译器将创建此函数的默认实现
Rect_& operator = (const Rect_& r) = default;
1)、返回自身是为了可以实现和cout一样连续调用
2)、为什么还要返回自身的引用呢?返回自身的引用是为了避免了一次拷贝构造和析构,提升程序性能
2. 右值引用和左值引用
1)什么是左值或者右值?
如果一个表达式能取其地址那么它就是左值,如果不能那么它就是右值。左值可以出现在等号左边或者右边,右值只能出现在等号的右边。
右值:
a.除了字符串之外的字面值
b.返回类型为非引用的函数调用
c.算术表达式
d.无名对象
2)左值引用就是一个不能变的指针,所以说在定义的时候就需要给它初始化,因为初始化后,它就一直引用该变量,不会再引用别的变量了。所以如果用浅拷贝的话,将亡变量消亡后,左值引用的这个变量也会跟着被消亡。
int b = 5
int& a = b //a为左值引用,之后a就不能引用别的变量了
3)右值引用的诞生是为了利用偷资源提高效率,偷资源的意思就是将亡值将被销毁掉时,我们可以重用将亡值的内存。在c++11中,右值(将亡值)作为参数就会优先匹配到一个右值引用的重载,左值作为参数就会调用左值引用的重载。
#include <iostream>
using namespace std;
class Matrix {
public:
Matrix(int _row, int _col) {
cout << "construction call" << endl;
row = _row;
col = _col;
//动态分配出一个大小为row*sizeof(float*)字节的内存空间,并将该内存空间的其实地址赋值给data
data = new float* [row]; //数组的元素是float*类型,数组名[下标] 相当于解引用
for (int i = 0; i < row; i++) {
//总结:通过数组的首地址来引用数组元素有两种方法:
//①数组名[下标] ,数组名[下标] 相当于解引用 ②(数组名 + 下标)
data[i] = new float[col];//确定每个float类型的指针指向空间的大小
for (int j = 0; j < col; j++) {
data[i][j] = 0;
}
}
}
//移动构造函数的参数是右值引用,这种右值引用的参数表示在函数调用之后就会被销毁掉,
//所以在这个函数中可以重用这个参数的资源来达到提升性能的作用,因为在移动构造函数中并没有分配新的内存也没有做大面积的拷贝工作
Matrix(Matrix&& mat) {
cout << "move construction call" << endl;
row = mat.row;
col = mat.col;
data = mat.data;
mat.data = nullptr;
}
Matrix(const Matrix& mat) {
cout << "copy construction call" << endl;
row = mat.row;
col = mat.col;
data = new float* [row];
for (int i = 0; i < row; i++) {
data[i] = new float[col];
for (int j = 0; j < col; j++) {
data[i][j] = mat.data[i][j];
}
}
}
//Matrix operator+(const Matrix& mat) {
// if (mat.row != row || mat.col != col) {
// printf("ERROR\n");
// }
// Matrix res(mat.row, mat.col);//结果
// for (int i = 0; i < mat.row; i++) {
// for (int j = 0; j < mat.col; j++) {
// res.data[i][j] = data[i][j] + mat.data[i][j];
// }
// }
// return res;
//}
//运算符重载为友元函数,右值引用,a在函数调用完就会被销毁掉,所以我们可以重用a的内存
friend Matrix operator +(Matrix&& a, Matrix&b) {
cout << "right value friend Matrix operator + call" << endl;
if (a.row != b.row || a.col != b.col) {
printf("ERROR\n");
}
//Matrix res(a.row, a.col);//结果
// Matrix&& m = getMatrix(),这里getMatrix()返回的是一个将亡值,m是一个右值引用,但m在等号左边,所以m是个类型为右值引用的左值
//Matrix res = a; //这一行代码会调用拷贝构造函数而不是移动构造函数,因为a是右值引用类型的左值
Matrix res = std::move(a);
for (int i = 0; i < a.row; i++) {
for (int j = 0; j < a.col; j++) {
//res.data[i][j] = a.data[i][j] + b.data[i][j];
res.data[i][j] = res.data[i][j]+ b.data[i][j];
}
}
return res;
}
//运算符重载为友元函数
friend Matrix operator +(const Matrix& a, const Matrix& b) {
cout << "friend Matrix operator + call" << endl;
if (a.row != b.row || a.col != b.col) {
printf("ERROR\n");
}
Matrix res(a.row, a.col);//结果
for (int i = 0; i < a.row; i++) {
for (int j = 0; j < a.col; j++) {
res.data[i][j] = a.data[i][j] + b.data[i][j];
}
}
return res;
}
~Matrix() {
if (data != nullptr) {
for (int i = 0; i < row; i++) {
if (data[i]){
delete[] data[i];
data[i] = nullptr;
}
}
delete[] data;
data = nullptr;
}
}
int row = 0;
int col = 0;
float** data = nullptr;
};
int main()
{
Matrix a(3, 4);
Matrix b(3, 4);
Matrix c(3, 4);
Matrix d(3, 4);
Matrix r = a + b + c+ d;
cout << "sssss" << endl;
}
补充知识点:
1.万能引用。注意区分万能引用和右值引用,只有形如 T && 的形式才是万能引用
2.C++11多了一种成员叫类型成员,访问它和访问静态成员一样用::来访问,什么是类型成员呢?比如
typedef T type;
的type就是类型成员 。
3.typename关键字(等同于class关键字):对于用于模板定义的依赖于模板参数的名称,只有在实例化的参数中存在这个类型名,或者这个名称前使用了 typename 关键字来修饰,编译器才会将该名称当成是类型。
4.一般来说,C++ 中的临时变量在表达式结束返回了一个值之后就被会销毁,比如Add add1=Add(),表示Add()表达式(一个表达式是C++中计算得到一个值的代码片段)返回一个临时对象,这个临时对象被用来初始化add1,但是在这个语句执行完成后,这个临时对象就会被销毁掉,这行代码调用的是移动构造函数而不是拷贝构造函数,移动构造函数会把原对象的内存资源转移给新对象。但也有例外的时候,如果这个临时变量被用来初始化一个常引用的话,那这个临时变量的生命周期就会被延长,直到常引用被销毁
5.c++中不带const的量可以自动转换成带const的量,反之则不行