C++转移构造函数与转移赋值函数

C++临时对象的定义:

当且仅当离开一段上下文(context)时在对象上执行的仅有的操作是析构函数时,一个对象被看成是临时的。这里上下文可能是一个表达式,也可能是一个语句范围,例如函数体。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++智能指针 智能指针 智能指针概念 C/C++ 语⾔最为⼈所诟病的特性之⼀就是存在内存泄露问题,因此后来的⼤多数语⾔都提供了内置内存分配与释放功能,有的甚⾄⼲脆对语 ⾔的使⽤者屏蔽了内存指针这⼀概念。这⾥不置贬褒,⼿动分配内存与⼿动释放内存有利也有弊,⾃动分配内存和⾃动释放内存亦如此,这 是两种不同的设计哲学。有⼈认为,内存如此重要的东西怎么能放⼼交给⽤户去管理呢?⽽另外⼀些⼈则认为,内存如此重要的东西怎么能 放⼼交给系统去管理呢?在 C/C++ 语⾔中,内存泄露的问题⼀直困扰着⼴⼤的开发者,因此各类库和⼯具的⼀直在努⼒尝试各种⽅法去检 测和避免内存泄露,如 boost,智能指针技术应运⽽⽣。 智能指针主要⽤于管理在堆上分配的内存,它将普通的指针封装为⼀个栈对象。当栈对象的⽣存周期结束后,会在析构函数中释放掉申请的 内存,从⽽防⽌内存泄漏。简要的说,智能指针利⽤了 C++ 的 RAII 机制,在智能指针对象作⽤域结束后,会⾃动做内存释放的相关操作, 不需要我们再⼿动去操作内存。 RAII是C++的发明者Bjarne Stroustrup提出的概念,RAII全称是"Resource Acquisition is Initialization",直译过来是"资源获取即初始化",也 就是说在构造函数中申请分配资源,在析构函数中释放资源。因为C++的语⾔机制保证了,当⼀个对象创建的时候,⾃动调⽤构造函数,当 对象超出作⽤域的时候会⾃动调⽤析构函数。所以,在RAII的指导下,我们应该使⽤类来管理资源,将资源和对象的⽣命周期绑定。 C++ 中有四种智能指针:auto_pt、unique_ptr、shared_ptr、weak_ptr 其中后三个是 C++11 ⽀持,第⼀个已经被 C++11 弃⽤且被 unique_prt 代替,不推荐使⽤。下⽂将对其逐个说明。 std::auto_ptr 在这个年代讨论 std::auto_ptr 不免有点让⼈怀疑是不是有点过时了,确实如此,随着 C++11 标准的出现(最新标准是 C++20),std::auto_ptr 已经被彻底放弃,取⽽代之是 std::unique_ptr。然⽽,之所以还向介绍⼀下 std::auto_ptr 的⽤法以及它的设计不⾜ 之处是想更多了解 C++ 语⾔中智能指针的发展过程,⼀项技术如果我们了解它过去的样⼦和发展的轨迹,我们就能更好地掌握它。 std::auto_ptr 的基本⽤法如下代码所⽰: #include <iostream> #include <memory> using namespace std; int main() { //初始化⽅式1 std::auto_ptr<int> ap1(new int(8)); //初始化⽅式2 std::auto_ptr<int> ap2; ap2.reset(new int(8)); cout << *ap1 << ", " << *ap2 << endl; return 0; } 输出: 8, 8 智能指针对象 ap1 和 ap2 均持有⼀个在堆上分配 int 对象,其值均是 8,这两块堆内存均可以在 ap1 和 ap2 释放时得到释放。这是 std::auto_ptr 的基本⽤法。 std::auto_ptr 真正让⼈容易误⽤的地⽅是其不常⽤的复制语义,即当复制⼀个 std::auto_ptr 对象时(拷贝复制或 operator= 复制),原对象 所持有的堆内存对象也会转移给复制出来的对象。⽰例代码如下: #include <memory> int main() { //测试拷贝构造 std::auto_ptr<int> ap1(new int(8)); std::auto_ptr<int> ap2(ap1); if (ap1.get() != NULL) { std::cout << "ap1 is not empty." << std::endl; } else { std::cout << "ap1 is empty." << std::endl; } if (ap2.get() != NULL) { std::cout << "ap2 is not empty." << std::endl; } else { std::cout << "ap2 is empty." << std::endl; } //测试赋值构造 std::auto_ptr<int> ap3(new int(8)); std::auto_ptr<int> ap4; ap4 = ap3; if (ap3.get() != NULL) { std::cout << "ap3 is not empty." << std::endl; }
C++智能指针的原理和实现 智能指针的原理和实现 ⼀、智能指针起因 ⼀、智能指针起因   在C++中,动态内存的管理是由程序员⾃⼰申请和释放的,⽤⼀对运算符完成:new和delete。   new:在动态内存中为对象分配⼀块空间并返回⼀个指向该对象的指针;   delete:指向⼀个动态独享的指针,销毁对象,并释放与之关联的内存。   使⽤堆内存是⾮常频繁的操作,容易造成堆内存泄露、⼆次释放等问题,为了更加容易和更加安全的使⽤动态内存,C++11中引⼊了智 能指针的概念,⽅便管理堆内存,使得⾃动、异常安全的对象⽣存期管理可⾏。智能指针主要思想是RAII思想,"使⽤对象管理资源",在类 的构造函数中获取资源,在类的析构函数中释放资源。智能指针的⾏为类似常规指针,重要的区别是它负责⾃动释放所指向的对象。   RAII是Resource Acquisition Is Initialization的简称,即资源获取就是初始化:   1.定义⼀个类来封装资源的分配与释放;   2.构造函数中完成资源的分配及初始化;   3.析构函数中完成资源的清理,可以保证资源的正确初始化和释放;   4.如果对象是⽤声明的⽅式在栈上创建局部对象,那么RAII机制就会正常⼯作,当离开作⽤域对象会⾃动销毁⽽调⽤析构函数释放资 源。 ⼆、智能指针类型 ⼆、智能指针类型   智能指针在C++11版本之后提供,包含在头⽂件<memory>中,标准命名std空间下,有auto_ptr、shared_ptr、weak_ptr、unique_ptr四 种,其中auto_ptr已被弃⽤。   :拥有严格对象所有权语义的智能指针;   :拥有共享对象所有权语义的智能指针;   :到 shared_ptr 所管理对象的弱引⽤;   :拥有独有对象所有权语义的智能指针。 2.1 auto_ptr   auto_ptr是通过由 new 表达式获得的对象,并在auto_ptr⾃⾝被销毁时删除该对象的智能指针,它可⽤于为动态分配的对象提供异常安 全、传递动态分配对象的所有权给函数和从函数返回动态分配的对象,是⼀个轻量级的智能指针,适合⽤来管理⽣命周期⽐较短或者不会被 远距离传递的动态对象,最好是局限于某个函数内部或者是某个类的内部。   声明:   template< class T > class auto_ptr;   template<> class auto_ptr<void>; // 对类型void特化     成员函数:   (1) : 获得内部对象的指针;   (2) :释放被管理对象的所有权,将内部指针置为空,返回内部对象的指针,此指针需要⼿动释放;   (3) :销毁内部对象并接受新的对象的所有权;   (4) :从另⼀auto_ptr转移所有权;   (5) 和:访问被管理对象。   注意事项:   (1) 其构造函数被声明为explicit,因此不能使⽤赋值运算符对其赋值,即不能使⽤类似这样的形式 auto_ptr<int> p = new int;   (2) auto_ptr 的对象所有权是独占性的,使⽤拷贝构造和赋值操作符时,会造成对象所有权的转移,被拷贝对象在拷贝过程中被修改;   (3) 基于第⼆条,因此不能将auto_ptr放⼊到标准容器中或作为容器的成员;   (4) auto_ptr不能指向数组,释放时⽆法确定是数组指针还是普通指针;   (5) 不能把⼀个原⽣指针交给两个智能指针对象管理,对其它智能指针也是如此。   auto_ptr是最早期的智能指针,在C++11 中已被弃⽤,C++17 中移除,建议使⽤unique_ptr代替auto_ptr。   简单实现: 1 template<class T> 2 class AutoPointer 3 { 4 public: 5 AutoPointer(T* ptr) 6 :mPointer(ptr){} 7 8 AutoPointer(AutoPointer<T>& other) 9 { 10 mPointer= other.mPointer; //管理权进⾏转移 11 other.mPointer= NULL; 12 } 13 14 AutoPointer& operator = (AutoPointer<T>& other) 15 { 16 if(this != &other) 17 { 18 delete mPointer; 19 mPointer = other.mPointer; //管理权进⾏转移 20 other.mPointer= NULL; 21 } 22 23 return *this; 24 } 25 26 ~AutoPointer() 27 { 28 delete mP
C++智能指针:auto_ptr详解 指针,相信⼤家并不陌⽣。⽆论是我们在进⾏查看内存还是在修改字符串,我们都会⽤到指针。 最常见的情况则是我们使⽤malloc或者new申请到了⼀块内存,然后⽤⼀个指针来保存起来。我们都知道有内存的申请那就必须要对它进⾏ 释放的处理,否则会造成最严重的后果——内存泄漏。⼀个或者两个申请的内存我们或许不会忘记去释放,但是如果成千上万⾏代码,你还 能记得住哪个释放了哪个没有释放吗? ⽽智能指针就是为了解决这个问题⽽产⽣的。最开始智能指针是在boost库中的,随着时间发展现在已经成为了C11的特性。(虽然我们本 篇要介绍的最基础的auto_ptr在C++11中已经被unique_ptr替代了。。) 智能指针的基本原理 智能指针其实是⼀个类,可以通过将普通指针作为参数传⼊智能指针的构造函数实现绑定。只不过通过运算符重载让它"假装"是⼀个指 针,也可以进⾏解引⽤等操作。既然智能指针是⼀个类,对象都存在于栈上,那么创建出来的对象在出作⽤域的时候(函数或者程序结束)会 ⾃⼰消亡,所以在这个类中的析构函数中写上delete就可以完成智能的内存回收。 Auto_ptr详解 使⽤时,需要包含头⽂件:memory。 auto_ptr,作为智能指针的始祖,能基本实现我们所期望的功能。⽽且设计简单源码易懂,虽然缺陷众多,但作为了解智能指针的研究对象 还是⼗分合适的。 ⾸先我们先来写⼀个测试类⽤于分析。 #include <iostream> #include <memory> using namespace std; class Test { public: Test(int param = 0) //调⽤时,可以指定是第⼏个对象。默认第0个 { num = param; cout << "Simple:" << num << endl; } ~Test() //析构时会指出第⼏个对象被析构。 { cout << "~Simple:" << num << endl; } void PrintSomething() //输出附加的字符串⽅法。 { cout << "PrintSomething:" << info_extend.c_str() << endl; } int num; //代表这个对象的序号。 string info_extend; //附加的字符串。 }; 接下来,我们通过⼏个测试函数来实验⼀下auto_ptr的基本功能,再来了解⼀下它的弊端。 I.Auto_ptr的基本功能 的基本功能 void TestAutoPtr1() { auto_ptr<Test> my_auto (new Test(1)); //绑定⼀个Test类的新建对象 if(my_auto.get()) //get函数⽤来显式返回它拥有的对象指针,此处判⾮空 { my_auto->PrintSomething(); my_auto.get()->info_extend = "Addition"; //对类内成员进⾏操作 my_auto->PrintSomething(); //看是否成功操作 (*my_auto).info_extend += " other"; //实验解引⽤操作 mt_auto->PrintSomething(); } } 运⾏结果: 我们可以看到在绑定时输出Simple:1,之后也能正常实现Test类中的功能,同时my_auto可以通过get⽅法进⾏裸指针赋值以及使⽤*进⾏ 解引⽤操作,与普通指针⽆异。最后函数结束后,在调⽤析构函数的同时auto_ptr的析构函数也将Test类的对象delete掉,我加⼊的内存泄 漏探测⼯具也显⽰No memory leaks detected(没有检查测到内存泄漏). II.Auto_ptr的弊端 的弊端1 void TestAutoPtr2() { auto_ptr<Test> my_auto1 (new Test(1)); if(my_auto1.get()) { (*my_auto1).info_extend = "Test"; //先赋予⼀个初始值作为区别两个指针的标志 auto_ptr<Test> my_auto2; my_auto2 = my_auto1; my_auto2->PrintSomething(); //打印发现成功转移 my_auto1->PringSomething(); //崩溃 } } 在这个程序的最后⼀步会崩溃,罪魁祸⾸就是my_auto2 = my_auto1语句。 autp_ptr对赋值运算符重载的实现是reset(Myptr.release()),即reset和release函数的组合。release会释放所有权,reset则是⽤于接受
移动构造函数C++11引入的一个新特性,通过移动构造函数,可以将一个对象的资源所有权转移到另一个对象,避免了不必要的资源拷贝,提高了程序的运行效率。 移动构造函数的定义如下: ```cpp class ClassA { public: ClassA(ClassA&& other) noexcept { // 移动构造函数的实现 } }; ``` 其中,`ClassA&&`表示移动构造函数的参数为右值引用。移动构造函数通常使用`noexcept`关键字进行修饰,表示该函数不会抛出异常。 移动构造函数的实现需要将另一个对象的资源转移到当前对象,并将另一个对象的资源置为无效。常见的实现方式是通过移动构造函数中的`std::move()`函数来完成。例如,对于一个包含动态分配内存的类: ```cpp class MyClass { public: MyClass(int n) : data(new int[n]), size(n) {} MyClass(MyClass&& other) noexcept : data(other.data), size(other.size) { other.data = nullptr; other.size = 0; } ~MyClass() { delete[] data; } private: int* data; int size; }; ``` 在移动构造函数中,将另一个对象的指针和大小赋值给当前对象,并将另一个对象的指针置为nullptr,大小置为0,表示另一个对象的资源已经被移动到了当前对象。 使用移动构造函数可以大大提高程序的性能,特别是在涉及到大量的资源拷贝时。移动构造函数可以通过`std::move()`函数进行调用,例如: ```cpp MyClass a(10); MyClass b(std::move(a)); // 调用移动构造函数 ``` 在这个例子中,对象a的资源被移动到了对象b中,对象a的资源已经无效了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值