当前位置: 首页 > news >正文

佛山自动机设备骏域网站建设专家wordpress 主页调整

佛山自动机设备骏域网站建设专家,wordpress 主页调整,定制的网站源码,优化大师官方下载文章目录三种对象的分类三种内存的区别动态内存概念智能指针允许的操作智能指针的使用规范new概念内存耗尽/定位new初始化默认初始化直接初始化值初始化delete概念手动释放动态对象空悬指针shared_ptr类格式独有的操作make_shared函数shared_ptr的计数器通过new用普通指针初始化… 文章目录三种对象的分类三种内存的区别动态内存概念智能指针允许的操作智能指针的使用规范new概念内存耗尽/定位new初始化默认初始化直接初始化值初始化delete概念手动释放动态对象空悬指针shared_ptr类格式独有的操作make_shared函数shared_ptr的计数器通过new用普通指针初始化shared_ptrunique_ptr概念、初始化、特性支持的操作weak_ptr概念关于普通指针和智能指针不能使用内置指针来访问shared_ptr所指向的内存get()函数reset函数处理异常动态数组概念new分配对象数组两种方法new的返回值初始化动态分配一个空数组是合法的释放动态数组动态数组和unique_ptr动态数组和shared_ptrallocator类new的局限性使用allocator的原因概念即创建销毁操作constructdestroydeallocate两个伴随算法使用了动态生存期的资源的类实例三种对象的分类 三种对象 全局对象在程序启动时分配在程序结束时销毁。局部自动对象当我们进入其定义所在的程序块时被创建在离开块时销毁。局部static对象在第一次使用前分配在程序结束时销毁。 三种内存的区别 静态存储区主要存放static静态变量、全局变量、常量。这些数据内存在编译的时候就已经为他们分配好了内存生命周期是整个程序从运行到结束。栈区存放局部变量。在执行函数的时候包括main这样的函数函数内的局部变量的存储单元会在栈上创建函数执行完自动释放生命周期是从该函数的开始执行到结束。线性结构。堆区程序员自己申请的任意大小的内存。一直存在直到被释放。链表结构。 前两种内存中的将对象由编译器自动创建和销毁。堆也被称作自由空间被用来存储动态分配的对象程序运行时分配的对象动态对象的生存期由程序来控制——当动态对象不被使用时必须显式地消灭他们。 动态内存 概念 为什么要使用动态内存 程序不知道自己需要使用多少对象程序不知道所需对象的准确类型程序需要在多个对象间共享数据 动态内存的分配与释放通过一对运算符来完成 new在动态内存中为对象分配空间并返回一个指向该对象的指针可以选择对对象进行初始化delete接受一个动态对象的指针销毁该对象并释放与之关联的内存。 使用动态内存时容易出现的问题 忘记释放内存产生的内存泄漏。这种内存永远不可能被归还给自由空间了。查找本错误是非常困难的通常应用程序运行很长时间之后真正耗尽内存时才能检测到这种错误。在尚有指针引用内存的情况下释放内存产生引用非法内存的指针释放一个已经被delete的内存产生double free的问题。出现此操作时自由空间就可能被破坏。 为了避免上述问题c11提供了两种智能指针smart pointer类型来管理动态对象两种指针的区别在于管理底层指针的方式 shared_ptr允许多个指针指向同一个对象unique_ptr独占所指向的对象 除此之外标准库还定义了一个名为weak_ptr的伴随类他是一种弱引用指向shared_ptr所管理的对象。这三种类型都定义在memory头文件中。 智能指针允许的操作 智能指针的使用规范 不使用相同的内置指针值初始化或reset多个智能指针。不delete get返回的指针。不使用get初始化或reset另一个智能指针。如果你使用get返回的指针记住当最后一个对应的智能指针销毁后你的指针就变为无效了。如果你使用智能指针管理的资源不是new分配的内存记住传递给它一个删除器。 new 概念 在自由空间分配的内存是无名的因此new无法为其分配的对象命名而是返回一个指向该对象的指针 int *pi new int; // pi指向一个动态分配的、未初始化的无名对象当然用new分配const的对象也是合法的但是一个动态分配的const对象必须进行初始化 定义了默认构造函数的类类型const动态对象可以隐式初始化其他类型必须显式初始化由于分配的对象是const的因此new返回的指针是一个指向const的指针 内存耗尽/定位new 值得一提的是如果一个程序用光了它所有可用的内存new表达式就会失败。 默认情况下。如果new不能分配所要求的内存空间就会抛出一个bad_alloc的异常。可以改变使用new的方式来阻止它抛出异常 我们称上面形式的new未定位newplacement new定位new表达式允许我们向new传递额外的参数。上例中我们传递给它一个由标准库定义的名为nothrow的对象将nothrow传递给new意图是告诉它不能抛出异常。 bad_alloc和northrow都定义在头文件new中。 初始化 默认初始化 默认情况下动态分配的对象是默认初始化的这意味着内置类型或组合类型的对象的值将是未定义的而类类型对象将用默认构造函数进行初始化 直接初始化 为了避免未定义行为最好使用直接初始化的方式来初始化一个动态分配的对象 可以用圆括号可以用列表初始化 值初始化 也可以使用值初始化只需要直接在类型名之后跟一对空括号即可 值得一提的是 对于定义了自己的构造函数的类类型来说要求值初始化是没有意义的——不管采用什么形式对象都会通过默认构造函数来初始化。对于内置类型两种形式的差别就很大了值初始化的内置类型对象有着良好定义的值而默认初始化的对象的值则是未定义的。类似的对于类中那些依赖于编译器合成的默认构造函数的内置类型成员如果它们未在类内被初始化那么它们的值也是未定义的。 delete 概念 为了防止内存耗尽在动态内存使用完毕后必须通过delete表达式将动态内存归还给系统。 默认情况下shared_ptr和unique_ptr都使用delete释放指向的对象但也都允许重载默认的删除器delete。 delete执行两个动作 销毁给定的指针指向的对象释放对应的内存 delete表达式接受一个指针指向我们想要释放的对象该指针必须指向动态分配的内存或者是一个空指针。 通常情况下 编译器不能分辨一个指针指向静态还是动态分配的对象编译器不能分辨一个指针所指向的内存是否已经被释放 对于上述两种情况大多数编译器会编译通过尽管他们是错误的。 因此释放一块非new分配的内存或者将相同的指针值多次释放其行为是未定义的 另外const对象的值虽然不能够被改变但是其本身可以被销毁 const int *pci new const int(1024); delete pci; // 正确释放一个const对象手动释放动态对象 智能指针可以在计数值为0时自动释放动态对象而delete是一种手动释放动态对象的方式这就要求程序员不能忘记delete这一步骤。 与类类型不同内置类型的对象被销毁时什么也不会发生。 特别是当一个指针离开其作用域时它所指向的对象什么也不会发生。如果这个指针指向的是动态内存那么内存将不会被自动释放。 举个例子 foo *factory(T arg){return new Foo(arg); // 调用factory的对象负责释放动态内存 }void use_factory(T arg){Foo *p factory(arg); } // p离开了它的作用域但实际所指向的内存没有被释放本例中一旦use_factory返回程序就没有办法释放这块内存了。修正这个错误的唯一方法是在use_factory中记得释放内存 void use_factory(T arg){Foo *p factory(arg);delete p; }空悬指针 执行delete p;后p并不指向空指针相反的p的值指向的地址不变但不能再使用p处理该地址的内容指针失效也不能重复delete p。 此时p就变成了空悬指针dangling pointer即指向一块曾经保存数据对象但现在已经无效的内存的指针。 不能重复delete p 但是可以重复 delete 空指针 避免空悬指针有两种方法 在指针即将要离开其作用域之前释放掉它所关联的内存。这样在指针关联的内存被释放掉后就没有机会继续使用指针了。也可以在delete之后将nullptr赋予指针这样就清楚地指出指针不指向任何对象。 但重置指针地方法仍然不是完美的动态内存的一个基本问题是可能有多个指针指向相同的内存。在delete内存之后重置指针的方法只对这个指针有效对其他任何仍指向已释放的内存的指针是没有作用的然而在实际中查找只想相同内存地所有指针也是异常困难的 shared_ptr类 格式 shared_ptr类型默认初始化的智能指针中保存着一个空指针。 独有的操作 关于上面两表的具体操作将在下面的普通指针和智能指针中指出。 make_shared函数 shared_ptr可以协调对象的析构但这仅限于其自身的拷贝也是shared_ptr之间。因此最安全的分配和使用动态内存的方法是调用一个名为make_shared的标准库函数而不是new。这样我们就能在分配对象的同时就将shared_ptr与之绑定从而避免了无意中将同一块内存绑定到多个独立创建的shared_ptr上。 make_shared函数定义在头文件memory中。 功能在动态内存中分配一个对象并初始化它返回此对象的shared_ptr。 实例 调用make_sharedT时传递的参数必须与T的某个构造函数相匹配换言之调用make_shared的行为的底层操作其实是调用对应类型的构造函数。 当然用auto定义一个对象来保存make_shared的结果也是可以的 auto p make_sharedvectorstring();shared_ptr的计数器 因为shared_ptr允许多个指针指向同一个对象。因此每个shared_ptr都有一个关联的计数器通常称其为引用计数reference count用来记录有多少个其他shared_ptr指向相同的对象。 当 用一个shared_ptr初始化另一个shared_ptrshared_ptr作为参数传递给一个函数shared_ptr作为函数的返回值 时shared_ptr所关联的计数器就会递增。 当 给shared_ptr赋予一个新值旧值计数器递减新值计数器递增shared_ptr被销毁例如一个局部的shared_ptr离开其作用域 时shared_ptr所关联的计数器就会递减。 一旦一个shared_ptr的计数器变为0它就会自动释放自己所管理的对象。 由于在最后一个shared_ptr销毁前内存都不会释放 保证shared_ptr在无用之后不再保留就非常重要了。如果你忘记了销毁程序不再需要的shared_ptr程序仍会正确执行但会浪费内存。 share_ptr在无用之后仍然保留的一种可能情况是你将shared_ptr存放在一个容器中随后重排了容器从而不再需要某些元素。在这种情况下你应该确保用erase删除那些不再需要的shared_ptr元素。 通过new用普通指针初始化shared_ptr 可以使用new返回的指针来初始化智能指针。接受指针参数的智能指针构造函数是explicit的。因此我们不能进行内置指针到智能指针间的隐式转换必须使用直接初始化形式来初始化一个智能指针 p1的初始化隐式地要求编译器将一个new返回的int隐式转换成一个shared_ptr这是不被允许的。 同样的一个返回shared_ptr的函数不能在其返回语句中隐式转换一个普通指针 必须将shared_ptr显式绑定到一个想要返回的指针上 unique_ptr 概念、初始化、特性 某个时刻只能有一个unique_ptr指向一个给定对象。unique_ptr被销毁时它所指向的对象也被销毁。 unique_ptr没有类似make_shared的标准库函数。定义一个unique_ptr时需要将其绑定到一个new返回的指针上且必须采用直接初始化形式 unique_ptrdouble pb; unique_ptrint pi(new int(2));根据“独占”的特性unique_ptr不支持普通的拷贝或赋值操作 不能拷贝unique_ptr的规则有个例外可以拷贝或赋值一个将要被销毁的unique_ptr。 常见的例子是从函数返回一个unique_ptr 或者返回一个局部对象的拷贝 支持的操作 可以通过release或reset起到类似拷贝或赋值的作用 release会切断unique_ptr和它原来管理的对象间的联系返回的指针常被用来初始化另一个智能指针或给另一个智能指针赋值。但是如果不用另一个智能指针来保存release返回的指针就要记得手动释放资源 weak_ptr 概念 weak_ptr是一种不控制所指向对象生存期的智能指针它指向一个由shared_ptr管理的对象。 具有以下特点 将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数引用计数归零时即使仍有weak_ptr指向对象对象还是会被释放 由于对象可能不存在我们不能使用weak_ptr直接访问对象必须调用lock。因此可以这样使用 关于普通指针和智能指针 不能使用内置指针来访问shared_ptr所指向的内存 当将一个shared_ptr绑定到一个普通指针时我们就将内存的管理责任交给了这个shared_ptr不应该再使用内置指针来访问shared_ptr所指向的内存了。 使用一个内置指针来访问一个智能指针所负责的对象是很危险的因为我们无法知道对象何时会被销毁。 举例 对于上面的函数以智能指针作为参数以传值方式传递是安全的当process结束时ptr的引用计数为1因此虽然局部ptr被销毁但是ptr指向的内存不会被释放 但同时也可以传递给process一个用内置指针显式构造的临时shared_ptr。但是这样做的风险是很大的 process(x);结束时临时对象被销毁其引用计数为0指向的内存会被释放此时x变成了空悬指针。 get()函数 get函数返回一个内置指针指向智能指针管理的对象。 函数是为了这种情况设计的我们需要向不能使用智能指针的代码传递一个内置指针。 使用get返回的指针的代码不能delete此指针。 虽然编译器不会给出错误信息但是将另一个智能指针也绑定到get返回的指针上是错误的 上述代码中shared_ptrint(q);将另一个指针绑定到get返回的指针上会导致程序块结束时p指向的内存被释放p变成空悬指针。 reset函数 reset将一个新的指针赋予shared_ptr shared_ptrint p new int(1024); // error不能将一个普通指针赋予shared_ptr p.reset(new int(1024)); // 正确p指向一个新对象与赋值类似reset会更新引用计数在需要的时候可以释放p指向的对象。 reset成员经常与unique一起使用来控制多个shared_ptr共享的对象。在改变底层对象之前我们检查自己是否是当前对象仅有的用户。如果不是在改变之前要制作一份新的拷贝 if(!p.unique()){p.reset(new string(*p)); // 不是旧对象仅有的指针分配新拷贝}*p newVal; // 是旧对象仅有的指针直接改变对象的值因为不会再有别的指针访问旧对象处理异常 当处理异常时经常会使程序块过早结束也就是如果使用普通指针管理内存可能在遇到detele之前推出程序块 void f() {int *ip new int(2);// throw一个异常且在f中未被捕获delete ip; //没能正常退出因此无法调用本句释放内存 }如果ip是shared_ptr类型则不会出现内存泄漏的情况在程序块结束时自动释放内存。 动态数组 概念 动态数组并不是数组类型。 new和delete运算符一次分配/释放一个对象但某些应用需要一次为很多对象分配内存的功能。 使用容器的类可以使用默认版本的拷贝、赋值和析构操作。分配动态数组的类必须定义自己版本的操作在拷贝、复制以及销毁对象时管理所关联的内存。 new分配对象数组 两种方法 方法一在类型名之后跟一对方括号在其中指明要分配的对象的数目方括号中的大小必须是整形但不必是常量。 例如 // 调用get_size确定分配多少个int int *pia new int[get_size()]; // pia指向第一个int方法二也可以用一个表示数组类型的类型别名来分配一个数组这样new表达式中就不需要方括号了 typedef int arrT[10]; // arrT表示10个int的数组类型 int *p new arrT; // 分配一个10个int的数组p指向第一个int但编译器在执行这个表达式时还是会用new[] int *p new int[42];new的返回值 当用new分配一个数组时我们并未得到一个数组类型对象而是得到一个数组元素类型的指针。因此 不能对动态数组调用begin或end。这些函数使用数组维度来返回指向首元素和尾后元素的指针。不能用范围for语句来处理动态数组中的元素 初始化 默认情况下new分配的对象不管是单个的还是数组中的都是默认初始化的。也可以通过一对空括号进行值初始化 以及提供一个元素初始化器的花括号列表 初始化器数目小于元素数目剩余元素进行值初始化。初始化器数目大于元素数目new表达式失败不会分配任何内存。 new表达式失败时会抛出一个类型为bad_array_new_length的异常。类似bad_alloc定义在头文件new中。 值得一提的是 虽然我们用空括号对数组中元素进行值初始化但不能在括号中给出初始化器这意味着不能用auto分配数组。 因为auto是编译器根据初始化值来判断类型的使用auto就必须有初始值没有初始值这里是初始化器auto自然也就不可以用了。 动态分配一个空数组是合法的 虽然我们不能创建一个大小为0的静态数组对象但当n等于0时调用new[n]是合法的 当我们用new分配一个大小为0的数组时new返回一个合法的非空指针。此指针保证与new返回的其他任何指针都不相同。对于零长度的数组来说此指针就像尾后指针一样我们可以像使用尾后迭代器一样使用这个指针。可以用此指针进行比较操作但此指针不能解引用——毕竟它不指向任何元素。 释放动态数组 为了释放动态数组可以使用一种特殊的delete——在职阵前加上一个方括号对。 第二条语句销毁pa指向的数组中的元素并释放对应的内存。数组中的元素按逆序销毁即最后一个元素首先被销毁然后是倒数第二个依此类推。 当我们释放一个指向数组的指针时空方括号对是必需的它指示编译器此指针指向一个对象数组的第一个元素。如果我们在delete一个指向数组的指针时忽略了方括号或者在delete一个指向单一对象的指针时使用了方括号其行为是未定义的。 动态数组和unique_ptr 当一个unique_ptr指向一个数组时我们不能使用点和箭头成员运算符。毕竟unique_ptr指向的是一个数组而不是单个对象因此这些运算符是无意义的。我们可以使用下标运算符来访问数组中的元素 实例 类型说明符中的方括号int[]指出up指向一个int数组而不是一个int。由于up指向一个数组当up销毁它管理的指针时会自动使用delete[]。 动态数组和shared_ptr shared_ptr不直接支持管理动态数组。如果希望用shared_ptr管理必须提供自己的删除器 shared_ptr不直接支持动态数组管理这一特性会影响我们如何访问数组中的元素 shared_ptr未定义下标运算符而且智能指针类型不支持指针算术运算。 因此为了访问数组中的元素必须用get获取一个内置指针然后用它来访问数组元素。 allocator类 new的局限性使用allocator的原因 new将内存分配和对象构造组合在了一起。delete将对象析构和内存释放组合在了一起 上述特性在灵活性上是有一定局限性的。这样在分配单个对象时当然是好的可以明确知道对象应该有什么值。但是分配大块内存时我们希望将内存分配和对象构造分离。这意味着我们可以先分配大块内存只有在真正需要时才执行对性的创建操作。 实例 有如下问题 我们可能不需要n个string可能只用到了少量的string。因此我们可能创建了一些永远也用不到的对象。对于确实需要使用的对象每个都被赋值了两次第一次是在默认初始化时第二次是在赋值时。没有默认构造函数的类就不能动态分配数组了。 概念即创建销毁操作 allocator定义在头文件memory中。 allocator分配的内存是未构造的。还未构造对象的情况下就是用原始内存是错误的。 construct 构造对象是通过construct完成的 construct成员函数接受一个指针和零个或多个额外参数 在给定位置构造一个元素。额外参数用来初始化构造的对象。类似make_shared的参数这些额外参数必须是与构造的对象的类型相匹配的合法的初始化器 allocatorstring alloc; auto const p alloc.allocate(20); auto q p; // q指向最后构造的元素之后的位置 // p指向分配的内存的首地址 alloc.construct(q, 5, x); // *p为xxxxx cout *p endl; // 正确使用string的输出运算符 cout *q endl; // 灾难q指向未构造的内存为了理解上面的代码用下面的代码查看一下构造对象前后p和q分别指向的地址 可以看到p一直指向分配的内存的首地址q指向最后构造的元素之后的地址因此 *p 可以访问已经构造的对象而 *p 访问的是未构造对象的原始内存这种行为是错误的。 destroy 用完对象后必须对每个构造的元素调用destory来销毁它们。 destory接受一个指针对指向的对象执行析构函数 while (q ! p) {alloc.destroy(--q); // 释放我们真正构造的string }不妨来查看一下执行上述代码之后的地址指向情况即内存分配的对象的值 可以看到执行完while之后q指向的地址已经和p一样了而再访问p中的对象——执行*p也无法输出”xxxxx“了。 但是在atom里面尝试运行的时候发现和预期的不一样。。。。destroy之后解引用p仍然能得到”xxxxx“ 吐槽同一段代码在不同的编译器上得到的结果不同猜测可能是底层的编译环境不同导致的。 emmmmm……还是更倾向于相信vs的运行结果如果有大佬看到这个问题知道原因的话请不吝赐教孩子实在不知道为什么会这样。 我们只能对真正构造了的元素进行destory操作。一旦元素被销毁后可以重新使用这部分内存来保存其他的string也可将内存归还给系统。 deallocate 释放内存通过deallocate来完成 alloc.deallocate(p, 20);传递给deallocate的指针不能为空必须指向由allocate分配的内存。传递给deallocate的大小参数必须与调用allocate分配内存时提供的大小参数具有一样的值。 两个伴随算法 用来初始化内存中创建的对象 实例 uninitialized_copy返回递增后的目的位置迭代器指向最后一个构造元素之后的位置。 使用了动态生存期的资源的类 大多数类中分配的资源都与对应对象生存期一致。 例如每个vector(对象)“拥有”其自己的元素分配的资源。当我们拷贝一个vector时原vector和副本vector中的元素是相互分离的。 某些类分配的资源具有与原对象相独立的生存期可能一个资源被两个对象共同引用。 换言之如果两个对象共享底层的数据当某个对象被销毁时我们不能单方面地销毁底层数据。 实例 构建一个类A用share_ptr管理vectorstring #pragma once #include vector #include string #include memoryusing namespace std;class A{ public:A(): vs(make_sharedvectorstring()){} // 分配一个空的vectorA(initializer_liststring il): vs(make_sharedvectorstring(il)){}// 接受一个初始化器的花括号列表将il当作make_shared的参数初始化vs// 通过调用底层vector的成员函数来完成size、empty、push_back、pop_backvectorstring::size_type size() const{return vs-size();}bool empty() const{return vs-empty();}void push_back(const string s){vs-push_back(s);}// pop_back、front、back操作需要先检查操作对象是否为空void pop_back(){check(0, pop_back on empty A);vs-pop_back();}string front(){check(0, front on empty A);return vs-front();}string back(){check(0, back on empty A);return vs-back();}// 针对const的A对象的front和back函数重载const string front() const;const string back() const; private:shared_ptrvectorstring vs;// check函数提供判空功能如果操作对象为空抛出一个异常void check(vectorstring::size_type si, const string s) const{if(si vs-size()){throw out_of_range(s);}} };const string A::front() const{check(0, front on empty A);return vs-front(); } const string A::back() const{check(0, back on empty A);return vs-back(); }用一个简单的A的使用程序。测试类的正确性 #include iostreamusing namespace std;#include my_A.hint main(int argc, char const *argv[]) {A a1;{A a2 {a, an, the};a1 a2;a2.push_back(about);cout a2.size() endl;}cout a1.size() endl;cout a1.front() a1.back() endl;const A a3 a1;cout a3.front() a3.back() endl;return 0; }输出结果
http://www.pierceye.com/news/709371/

相关文章:

  • 苏州网站建设制作开发公司江浦做网站
  • 网站开发哪一门语言更快网站设计方案模板
  • 阿里云做网站需要些什么条件个人博客网站设计模板
  • 更改网站模板内容我赢职场wordpress
  • h5模板下载有哪些网站南京高端网站制作公司
  • 户外旅游网站模板佛山网络优化推广公司
  • 海南住房和城乡建设网站技术支持 重庆网站
  • 网站图片展示代码怎样给响应式网站提速
  • 学校 网站建设 招标广而告之微信推广平台
  • 企业如何通过地方网站宣传网站中国建设银行招聘官网
  • 上海品牌网站建设公网站的开发与建设项目
  • 做网站的艰辛电子商务网站建设与维护概述
  • 织梦网站做关键词网站开发到上线的过程
  • 威海千淼网站建设北京知名广告公司有哪些
  • wordpress多站点可视化wordpress主题flarum
  • 网站免费虚拟主机申请成华区微信网站建设公
  • 机械制造设备类企业网站织梦模板网站模板 博客
  • js跳转网站怎么做网络营销方式单一的原因
  • 做网站的职责做章的网站
  • 万网建设网站wordpress的ftp設置
  • 网站建设a云世家宋南南电子商务网站的开发方式
  • 水利工程建设监理网站美食网站建设总结
  • 中化建工北京建设投资有限公司网站南沙网站建设方案
  • 东莞网站制作网站死链是什么
  • 网站开发哪种语言更安全seopc流量排名官网
  • 中国站长之家域名查询深圳html5网站推广价格
  • 商业网站建设案例视频上海猎头公司哪家好
  • 如何开个人网站seo诊断站长
  • wordpress rss 订阅乐陵seo推广
  • 公司做一个网站企业建设3D网站