因此,在设计自己的类的使用,需要设计对应的Copy assignment for lvalue and Move assignment for rvalue.
实例
函数调用时避免复制
如果一个变量我们在传参进入函数之后就不再使用,可以使用std::move()来实现移动构造。
1 2 3 4 5 6 7 8
voidFun(std::vector<int> v1, std::vector<int> v2){ // Use vector(const vector &other) as constructor Foo(v1); // v1 can still be used after this // Use vector(vector &&other) as constructor Foo(std::move(v2)); // v2 cannot be used after this }
/* * @Author: Xiyuan Yang xiyuan_yang@outlook.com * @Date: 2025-04-13 18:54:39 * @LastEditors: Xiyuan Yang xiyuan_yang@outlook.com * @LastEditTime: 2025-04-13 23:35:26 * @FilePath: /20250413_modern_cpp/move.cpp * @Description: * Do you code and make progress today? * Copyright (c) 2025 by Xiyuan Yang, All Rights Reserved. */ #include<iostream> #include<utility>// for std::move
/* * @Author: Xiyuan Yang xiyuan_yang@outlook.com * @Date: 2025-04-14 00:03:41 * @LastEditors: Xiyuan Yang xiyuan_yang@outlook.com * @LastEditTime: 2025-04-14 00:13:32 * @FilePath: /20250413_modern_cpp/universal.cpp * @Description: * Do you code and make progress today? * Copyright (c) 2025 by Xiyuan Yang, All Rights Reserved. */ #include<iostream> #include<type_traits>
template <typename T> voidtest(T &¶m){ ifconstexpr(std::is_lvalue_reference<T>::value){ std::cout << "param is an lvalue" << std::endl; } else { std::cout << "param is an rvalue" << std::endl; } }
intmain(){ int a = 10; int *b = &a; constint *c = b; constint d = 10;
/* * @Author: Xiyuan Yang xiyuan_yang@outlook.com * @Date: 2025-04-13 19:07:20 * @LastEditors: Xiyuan Yang xiyuan_yang@outlook.com * @LastEditTime: 2025-04-14 00:18:51 * @FilePath: /20250413_modern_cpp/forward.cpp * @Description: * Do you code and make progress today? * Copyright (c) 2025 by Xiyuan Yang, All Rights Reserved. */ #include<iostream> #include<string> classA { public: A(const std::string &s) : s_(s) { std::cout << "const string &" << std::endl; } A(std::string &&s) : s_(std::move(s)) { std::cout << "string &&" << std::endl; }
private: std::string s_; }; template <classT> voidfoo(T &&s){ ifconstexpr(std::is_lvalue_reference<T>::value){ std::cout << "param is an lvalue" << std::endl; } else { std::cout << "param is an rvalue" << std::endl; } A tmp(s); }
intmain(){ std::string a = "111"; std::string b = "222"; foo(a); foo(a + b); foo(std::string{"111222"}); }
很惊讶的是,程序的输出竟然是这样:
1 2 3 4 5 6
param is an lvalue conststring & param is an rvalue conststring & param is an rvalue conststring &
为什么左值和右值都选择了使用const string&?原因在于const T&在构造函数中相比于右值构造具有更高的优先级。也就是说在const T&存在的情况下,传递一个右值会将参数传递给左值const引用并且延长他的生命周期。但我们可以使用std::forward来强制保证 Passes an rvalue: Ensures that an rvalue reference is passed.
具体而言,我们可以修改代码:A tmp(std::forward<T>(s));
得到下面的输出示例:
1 2 3 4 5 6
param is an lvalue conststring & param is an rvalue string && param is an rvalue string &&
auto reference
自动类型推断:
auto 可以推出变量类型
auto & 可以推出引用类型
const auto & 可以推出常量引用
同时,auto还支持对数组,元组的自动解包和打包。
同样的,我们也可以结合for循环中的代码使用auto:
1 2 3 4 5 6
std::map<int, std::string> GetMap(); intmain(){ auto map = GetMap(); for (constauto &[key, value] : map) {} // Do something }
/* * @Author: Xiyuan Yang xiyuan_yang@outlook.com * @Date: 2025-04-13 19:33:09 * @LastEditors: Xiyuan Yang xiyuan_yang@outlook.com * @LastEditTime: 2025-04-13 21:02:38 * @FilePath: /20250413_modern_cpp/ptr.cpp * @Description: * Do you code and make progress today? * Copyright (c) 2025 by Xiyuan Yang, All Rights Reserved. */
3_modern_cpp/"ptr Constructed Name: Shared_ptr ptr1's name Shared_ptr ptr2's name Shared_ptr ptr1 is not null now! ptr1 is null now! ptr3's name Shared_ptr This object's name is Shared_ptr Destructed
❯ valgrind --leak-check=full ./ptr ==13994== Memcheck, a memory error detector ==13994== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al. ==13994== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info ==13994== Command: ./ptr ==13994== Node created Node created ==13994== ==13994== HEAP SUMMARY: ==13994== in use at exit: 64 bytes in 2 blocks ==13994== total heap usage: 4 allocs, 2 frees, 74,816 bytes allocated ==13994== ==13994== 64 (32 direct, 32 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 2 ==13994== at 0x4846FA3: operator new(unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==13994== by 0x10CAB1: std::__new_allocator<std::_Sp_counted_ptr_inplace<Node, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long, void const*) (in /home/xiyuanyang/ACM_course_DS/20250413_modern_cpp/ptr) ==13994== by 0x10C4A3: std::__allocated_ptr<std::allocator<std::_Sp_counted_ptr_inplace<Node, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> > > std::__allocate_guarded<std::allocator<std::_Sp_counted_ptr_inplace<Node, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> > >(std::allocator<std::_Sp_counted_ptr_inplace<Node, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> >&) (in /home/xiyuanyang/ACM_course_DS/20250413_modern_cpp/ptr) ==13994== by 0x10C0E4: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<Node, std::allocator<void>>(Node*&, std::_Sp_alloc_shared_tag<std::allocator<void> >) (in /home/xiyuanyang/ACM_course_DS/20250413_modern_cpp/ptr) ==13994== by 0x10BE3D: std::__shared_ptr<Node, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<void>>(std::_Sp_alloc_shared_tag<std::allocator<void> >) (in /home/xiyuanyang/ACM_course_DS/20250413_modern_cpp/ptr) ==13994== by 0x10BC5E: std::shared_ptr<Node>::shared_ptr<std::allocator<void>>(std::_Sp_alloc_shared_tag<std::allocator<void> >) (in /home/xiyuanyang/ACM_course_DS/20250413_modern_cpp/ptr) ==13994== by 0x10B571: std::shared_ptr<std::enable_if<!std::is_array<Node>::value, Node>::type> std::make_shared<Node>() (in /home/xiyuanyang/ACM_course_DS/20250413_modern_cpp/ptr) ==13994== by 0x10A936: test_bug_for_shared_ptr() (in /home/xiyuanyang/ACM_course_DS/20250413_modern_cpp/ptr) ==13994== by 0x10A9F1: main (in /home/xiyuanyang/ACM_course_DS/20250413_modern_cpp/ptr) ==13994== ==13994== LEAK SUMMARY: ==13994== definitely lost: 32 bytes in 1 blocks ==13994== indirectly lost: 32 bytes in 1 blocks ==13994== possibly lost: 0 bytes in 0 blocks ==13994== still reachable: 0 bytes in 0 blocks ==13994== suppressed: 0 bytes in 0 blocks ==13994== ==13994== For lists of detected and suppressed errors, rerun with: -s ==13994== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
NodeFix created NodeFix created NodeFix destroyed NodeFix destroyed
使用Valgrind也是安然无恙!😊
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
==17644== Memcheck, a memory error detector ==17644== Copyright (C)2002-2022, and GNU GPL'd, by Julian Seward et al. ==17644== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info ==17644== Command: ./ptr ==17644== NodeFix created NodeFix created NodeFix destroyed NodeFix destroyed ==17644== ==17644== HEAP SUMMARY: ==17644== in use at exit: 0 bytes in 0 blocks ==17644== total heap usage: 4 allocs, 4 frees, 74,848 bytes allocated ==17644== ==17644== All heap blocks were freed -- no leaks are possible ==17644== ==17644== For lists of detected and suppressed errors, rerun with: -s ==17644== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Rust 的所有权机制要求每个值都有唯一的所有者(通常是变量),并且在同一时间内只能有一个所有者。当变量的所有权被转移或其生命周期结束后,所有对该变量的借用(相当于 C++ 中指向变量的指针)都不能再被使用,否则会造成编译错误。Rust 的编译器可以在编译时就能“静态”地检查所有权和借用关系,从而在运行时无需额外检查的情况下保证了内存安全。
这种所有权机制虽然保证了内存安全,但是也使得一些数据结构的实现变得困难。因此,对于堆上对象的所有权,Rust 引入了类似于 C++ 中智能指针的机制,包括:
Rc<T>:相当于 C++ 中的 std::shared_ptr,允许多个所有者共享实际对象,通过引用计数管理内存
Weak<T>:相当于 C++ 中的 std::weak_ptr,不增加强引用计数,用于解决循环引用问题
// No destructor needed, memory managed by shared_ptr
// Check if list is empty boolempty()const{ return length == 0; }
// Get the size of the list size_tsize()const{ return length; }
// Add element to the front voidpush_front(const T& value){ auto new_node = std::make_shared<Node>(value); if (head) { new_node->next = head; head->prev = new_node; } else { tail = new_node; // If list was empty, update tail } head = new_node; length++; }
// Add element to the back voidpush_back(const T& value){ auto new_node = std::make_shared<Node>(value); if (tail.lock()) { auto tail_node = tail.lock(); tail_node->next = new_node; new_node->prev = tail_node; } else { head = new_node; // If list was empty, update head } tail = new_node; length++; }
// Remove and return the first element std::optional<T> pop_front(){ if (!head) { return std::nullopt; } auto value = head->value; head = head->next; if (head) { head->prev.reset(); // Clear the weak_ptr reference } else { tail.reset(); // If list becomes empty, clear tail } length--; return value; }
// Remove and return the last element std::optional<T> pop_back(){ if (empty()) { return std::nullopt; } auto tail_node = tail.lock(); auto value = tail_node->value; if (tail_node->prev.lock()) { auto prev_node = tail_node->prev.lock(); prev_node->next.reset(); tail = prev_node; } else { head.reset(); // If list becomes empty, clear head tail.reset(); } length--; return value; }
2024年:Rust不断发展壮大,自从 2022 年 Linux 内核宣布引入 Rust 语言以来,这个社区内部掀起了一场意料之外的风波。不久前,Rust for Linux 项目的核心维护者 Wedson Almeida Filho 决定退出该项目,他坦言已厌倦了社区中不断增多的、与技术无关的争论,其中不乏涉及 Rust 与 C 的语言之争。