随笔-C++编程(advance,ing...)

基础特性

this指针

1) xobj->func() 实际是OBJ::func(xobj);

2) 成员函数第一个参数默认都为指向对象的this指针,函数内部访问成员变量实际是this->xxx这样的形式

参考:C++中this指针的理解与作用详解 - 午夜逛街的黛玛的文章 - 知乎 https://zhuanlan.zhihu.com/p/95735331

A *p = NULL; // this指针和A *p都是指向当前对象的地址,这里没有对象,指针地址为NULL
p->Hello(); // 编译结果是直接调用代码段对应函数A::Hello(p);  PS:和C回调函数设置不太一样,这里不会为空
// 如果Hello() 长如下这样,则会崩溃
void Hello() { cout << i << "hello" << endl; } // ->> void Hello(A * this ) { cout << this->i << "hello" << endl; }

return this与return *this:

return *this - 若返回类型为A,则是克隆,若返回类型为A&,则是本身

参考:C++中this与*this的区别 https://blog.csdn.net/daimous/article/details/78618432

return this :返回当前对象的地址(指向当前对象的指针)
return *this:若返回类型为A, 则是克隆, 若返回类型为A&, 则是本身 )


引用的本质

语法糖,底层汇编是一样的,即int*等于int&,区别在于int&如果要赋值不用再*i=...这样写

且实参是T A或者T A&都可以(此处用int int &表示),实参是T A会自动转成T A&形式:


左值和右值(不是lvalue rvalue,是loactor value和read value)

左值的英文简写为“lvalue”,右值的英文简写为“rvalue”。很多人认为它们分别是"left value"、"right value" 的缩写,其实不然。

lvalue 是 "loactor value",意为内存中的存储空间
rvalue 是 "read value",指的是读取数据

左值引用和右值引用

右值引用是一个左值(loactor value)!,汇编两者都是一样的,都是指针,都是左值,右值引用存在的意义是为了能提供(移动构造函数和移动赋值运算符)方式

左值引用:classA&
右值引用:classA&& (但是是一个左值)

[1/3] 从汇编上看,是一样的:

[2/3] 那么右值引用的意义在哪里,从目前看的一些文章,理解就是“为了让类根据需要实现出移动拷贝构造函数和移动赋值运算符”

参考:C++: 左值引用(&), 右值引用(&&),万能引用(template &&)详解 与 完美转发(forward) 实现剖析 https://www.cnblogs.com/ishen/p/13771991.html

在c++11中提出了右值引用,作用是为了和左值引用区分开来,其作用是: 右值引用限制了其只能接收右值,可以利用这个特性从而提供重载,这是右值引用有且唯一的特性,限制了接收参数必为右值, 这点常用在move construct中,告诉别人这是一个即将消失的对象的引用,可以瓜分我的对象东西,除此之外,右值引用就没有别的特性了

参考:CPP开发者 【Modern C++】深入理解移动语义 https://mp.weixin.qq.com/s/1icLvk-RJmajpgU95mdOVQ

右值引用的主要用途是创建移动构造函数和移动赋值运算符。移动构造函数和拷贝构造函数一样,将对象的实例作为其参数,并从原始对象创建一个新的实例。
但是,移动构造函数可以避免内存重新分配,这是因为移动构造函数的参数是一个右值引用,也可以说是一个临时对象,而临时对象在调用之后就被销毁不再被使用,因此,在移动构造函数中对参数进行移动而不是拷贝。换句话说,右值引用和移动语义允许我们在使用临时对象时避免不必要的拷贝。
移动语义通过移动构造函数和移动赋值操作符实现,其与拷贝构造函数类似,区别如下:

  • 参数的符号必须为右值引用符号,即为&&
  • 参数不可以是常量,因为函数内需要修改参数的值
  • 参数的成员转移后需要修改(如改为nullptr),避免临时对象的析构函数将资源释放掉

[3/3] 看着移动构造函数的处理逻辑,也可以在拷贝构造函数里面做啊? 问题同:c++移动语义的作用到底是什么? - 知乎https://www.zhihu.com/question/487959729

A: 网上很多文章会将移动构造和拷贝构造进行对比,其实移动构造不是为了取代拷贝构造而发明的,是为了让一个类能同时支持这两种操作,在必要的时候。比如智能指针,C++11之前auto_ptr,很容易出错,在c++11加入右值引用规则后,普通复制调用拷贝构造函数,std::move则调用移动构造函数

其他参考:

参考:C++学习笔记:右值引用,移动和转发
参考:现代C++开发 右值和移动究竟解决了什么问题?

右值引用使用

能作为右值引用类型的参数:1) prvalue 临时变量 2) std:move(xxx) (是一个read value,为了跟prvalue区分,被特称为xvalue:) [编译器选择调用右值引用的接口]

能作为右值引用类型的参数有:

  1. prvalue 临时变量
  2. xvalue: std:move(xxx)

std:move(xxx)作用是把一个左值引用强制转换成一个右值引用,而并不改变其内容。等价于 static_cast<smart_ptr&&>(ptr1)。因此,std::move(ptr1) 的结果是指向 ptr1 的一个右值引用,这样构造 ptr2 时就会选择上面第二个重载。
可以把 std::move(ptr1) 看作是一个有名字的右值。为了跟无名的纯右值 prvalue 相区别,C++ 里目前就把这种表达式叫做 xvalue。跟左值 lvalue 不同,xvalue 仍然是不能取地址的——这点上,xvalue 和 prvalue 相同。所以,xvalue 和 prvalue 都被归为右值 rvalue。

示例:

据 C++ 的规则,如果我提供了移动构造函数而没有手动提供拷贝构造函数,那后者自动被禁用(记住,C++ 里那些复杂的规则也是为方便编程而设立的)。于是,我们自然地得到了以下结果:

smart_ptr<shape> ptr1{create_shape(shape_type::circle)};
smart_ptr<shape> ptr2{ptr1};             // 编译出错
smart_ptr<shape> ptr3;
ptr3 = ptr1;                             // 编译出错
ptr3 = std::move(ptr1);                  // OK,可以
smart_ptr<shape> ptr4{std::move(ptr3)};  // OK,可以

注意生命周期:prvalue如果绑定到一个引用上,它的生命周期则会延长到跟这个变量一样长,但是这条规则对xvalue无效

C++规则:一个临时对象会在包含这个临时对象的完整表达式估值完成后、按生成顺序的逆序被销毁,除非有生命周期延长发生

prvalue如果绑定到一个引用上,它的生命周期则会延长到跟这个变量一样长,但是这条规则对xvalue无效

result&& r = process_shape(circle(), triangle()); // ok
result&& r = std::move(process_shape(circle(), triangle())); // 异常,r指向的对象已经不存在

模版

定义模版 (有点像利用宏定义定义出了类或者方法)

posted @ 2024-03-22 14:26  LiYanbin  阅读(0)  评论(0编辑  收藏  举报