一、右值引用 右值:无法获取地址的值称为右值。 语法:类型&& 引用变量名称 = 右值; int&& a = 10;//a是右值10的引用 a = 100;
class String{ String(String &&s) { m_str = s.m_str; s.m_str = nullptr;//防止临时对象被delete } ~String() { if(nullptr == m_str) //防止临时对象被delete { delete[] m_str; } } private: char* m_str; };
String getStr(char* p) { String tmp(p); return tmp; }
int main() { String s(getStr("hello")); //其中调用拷贝构造函数都调用了 String(String &&s) } 二、lamda表达式(闭包或者匿名函数对象) 捕获变量可选限定符->返回类型{函数代码} 捕获类型分为 按值=和按引用&,默认是按值 返回类型可自行推断 int main() { int a = 3; int b = 4; auto p = [a,b](int x,int y)->int{return a * b + x + y;}; // 按值捕获 //auto p = [&a,&b](int x,int y)->int{return a * b + x + y;};//按引用捕获 [=,&y] cout << p(4,2) << endl; // 34+4+2 b = 5; cout << p(4,2) << endl; // 按值捕获是34+4+2,按引用捕获是3*5+4+2,按值捕获时表达式在初始化的时候复制了a和b的值 }
三、智能指针 3.1 unique_ptr(独占指针): 任何时刻只能有一个指针管理内存 当指针超出作用域时,内存将自动释放 该类型指针不可copy,只能move
创建方法: unique_ptr ptr(new A(参数)) unique_ptr ptr = make_unique(参数) 所有权转移的方法: unique_ptr ptr(new A(参数)); unique_ptr ptr2 = move(ptr); //ptr不能再使用,可能会出现段错误,unique_ptr作为函数参数时并按值传递要考虑所有权 ptr3 = ptr2;//错误,因为是独占的,也就是一个内存对应一个指针,赋值函数被delete了
void do_with_cat_pass_value(std :: unique_ptr<Cat> c)
{
}
void do_with_cat_pass_ref(std :: unique_ptr<Cat> &c)
{
}
int main(int argc, char *argv[])
// 1 pass value std :: unique_ptr c1 = make_unique("ff"); do_with_cat_pass_value(std :: move(c1)); //必须move // c1->cat_info(); // make_unique do_with_cat_pass_value(std :: make_unique());// 使用make_unique自带了move,不需要再move
// 2 pass ref std :: unique_ptr c2 = make_unique("f2"); I do_with_cat_pass_ref(c2); // 不需要move // c2.cat_info(); cout << "cat address" << c2.get() << endl;
cout << " ----- yz ------ " << endl; return 0; }
3.2 shared_ptr:计数指针又称共享指针,与unique_ptr不同的是它是可以共享数据的 use_count()用来打印计数
std::shared_prt<int> p = make_shared<int>(10);
std::shared_prt<int> p2 = p;
cout << p.use_count() << endl; //输出2
3.3 unique_ptr可以转换为shared_ptr,shared_ptr不能转换为unique_pt
std::unique_ptr test()
{
return std::make_unique(0);
}
int main()
{
std::shared_ptr ptr1 = test(); //test函数返回的是右值
std::unique_ptr ptr2 = std::make_unique(10);
ptr1 = std::move(ptr2);
std::cout << "Hello World";
return 0;
}
对于临时对象(右值),编译器会自动应用移动语义;对于命名变量(左值),需要显式使用 std::move() 来启用移动语义。
- test()函数返回一个std::unique_ptr的临时对象(右值)。
- 使用这个临时对象来构造一个std::shared_ptr,因为unique_ptr的临时对象是右值,所以可以调用shared_ptr的移动构造函数(从unique_ptr移动构造shared_ptr是允许的,因为unique_ptr可以释放所有权)。
- 因此,这里不需要显式使用std::move,因为test()返回的已经是右值
3.4 weak_ptr:主要用于循环依赖,使用shared_ptr相互依赖,计数无法清0,必须用weak_ptr,例如: class A{ public: void test(){}; private: std::weak_ptr m_ptr; }; 1.不能单独使用,要和shared_ptr配合使用 2.weak_ptr没有所有权,不能使用 -> 调用对象的成员函数 3.使用时计数不会加一 int main() { std::shared_ptr ptr1 =std::make_shared(); std::weak_ptr ptr2(ptr1); std::cout << ptr2.use_count() << std::endl; std::cout << ptr1.use_count() << std::endl; std::shared_ptr ptr3 = ptr2.lock();//lock函数把指针类型转换了 std::cout << ptr1.use_count() << std::endl;//输出是2 std::cout << ptr2.use_count() << std::endl;//输出是2 std::cout << ptr3.use_count() << std::endl;//输出是2 std::cout << "Hello World"; return 0; } 深拷贝:构造函数,拷贝构造函数、赋值操作符都要是深拷贝 class String{ public: String(char* str="") { if(nullptr == str) { str = ""; } m_str= new char[strlen(str) + 1]; strcpy(m_str, str); } String(const String& s) { m_str = new char[strlen(s.m_str)+1]; strcpy(m_str, s.m_str); } String& operator=(const String& s) { // 1. 先分配新内存 char* new_str = new char[strlen(s.m_str) + 1]; // 2. 拷贝数据到新内存 strcpy(new_str, s.m_str); // 3. 删除旧内存 delete[] m_str; // 4. 让成员指向新内存 m_str = new_str; return this; } private: char m_str; };