析构函数

小朋友学C++(17):析构函数

匿名 (未验证) 提交于 2019-12-03 00:26:01
析构函数(destructor) 与构造函数相反,当对象结束其生命周期时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,delete会自动调用析构函数后释放内存)。 析构函数名也应与类名相同,只是在函数名前面加一个位取反符~,例如~A( )。以区别于构造函数。 与构造函数一样,析构函数不能有返回值。不同的是,析构函数,也不能带参数,并且一个类只能有一个析构函数。 如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数。 许多简单的类中没有使用用显式的析构函数。 例子: #include <iostream> using namespace std ; class A { public : A() { cout << "Constructor method is invoked!" << endl; } ~A() { cout << "Deconstructor method is invoked!" << endl; } void sayHi() { cout << "Hi!" << endl; } }; int main() { A *a = new A(); a->sayHi(); delete a; return 0 ; } 运行结果: Constructor method

第四章习题

匿名 (未验证) 提交于 2019-12-03 00:22:01
38381415125 鲍安狄 15移动本 一、填空题 (1)如类果类A继承了B,那么类A被称为 基 类,而类B被称为 派生 类。 (2)C++的两种继承为: 单继承 和 多继承 。 (3)在默认情况下的继承方式为 私有继承方式 。 (4)从基类中公有派生一个类时,基类的公有成员就成为派生类的 公有 成员,而这个基类的保护成员就成为派生类的 保护 成员。 (5)C++提供了 多继承 机制,允许一个派生类可以继承多个基类,甚至这些基类是互不相关的。 (6)类X中的名字N 支配类Y中同名的名字N,是指类X以类Y为它的一个基类,这称为 支配规则 。 (7)引进虚基类的目的是 解决二义性 。 (8)在一个继承结构中,解决二义性的方法有 使用作用域运算符 和 引进虚基类 。 二、选择题(至少选一个,可以多选) (1)C++语言建立类族是通过( B )。 A.类的嵌套 B.类的继承 C.虚函数 D.抽象类 (2) 继承是( CD )的方法。 A.将特殊的类变成通用的类 B.将通用的参数传送给特殊的类的对象 C.将通用的类变成特殊的类 D.将已有的类添加新的特性,但不重写它们 (3)继承的优点是( ABC )。 A.扩大类的使用范围,更便于使用类库 B.避免重写程序代码,提供有用的概念框架 C.把类转化成有条理的层次结构 D.通过继承的自然选择和重写使类进一步拓展 (4)下面叙述不正确的是( C

浅谈 C++ 中的 new/delete 和 new[]/delete[]

匿名 (未验证) 提交于 2019-12-03 00:20:01
在 C++ 中,你也许经常使用 new 和 delete 来动态申请和释放内存,但你可曾想过以下问题呢? new 和 delete 是函数吗? new [] 和 delete [] 又是什么?什么时候用它们? 你知道 operator new 和 operator delete 吗? 为什么 new [] 出来的数组有时可以用 delete 释放有时又不行? … 如果你对这些问题都有疑问的话,不妨看看我这篇文章。 new 和 delete 到底是什么? 如果找工作的同学看一些面试的书,我相信都会遇到这样的题:sizeof 不是函数,然后举出一堆的理由来证明 sizeof 不是函数。在这里,和 sizeof 类似,new 和 delete 也不是函数,它们都是 C++ 定义的关键字,通过特定的语法可以组成表达式。和 sizeof 不同的是,sizeof 在编译时候就可以确定其返回值,new 和 delete 背后的机制则比较复杂。 继续往下之前,请你想想你认为 new 应该要做些什么?也许你第一反应是,new 不就和 C 语言中的 malloc 函数一样嘛,就用来动态申请空间的。你答对了一半,看看下面语句: string * ps = new string ( "hello world" ); expression operator new operator delete

对象生命周期管理

匿名 (未验证) 提交于 2019-12-03 00:19:01
先定义什么叫线程安全:就是能够在多线程环境下使用,不需要通过额外的同步代码。 要保证对象的构造线程安全,我们需要做到在构造期间不将this指针泄露出去,因为在多线程环境下,有可能其他线程会使用到未构造完成的this指针。 即使我们在构造函数的最后一行,也不应该将this指针传递给其他线程,因为c++中基类早于派生类进行构造,此时有可能还要进行派生类的构造。 问题:如何保证一个对象正在使用时不会被其他线程析构。 我们知道解决竞态的方法就是将所有操作排队,其中一种方法就是加锁,但是在多线程中,锁有可能会在析构函数中被释放,来看例子: 客户端代码: 在实际执行中,可能在Foo析构函数得到mutex锁之前,线程B已经在执行update了,并且阻塞在锁上,此时析构函数执行完后,锁就不存在了。程序行为未知。。。 从上面的例子我们看到了作为成员变量的mutex是无法保护析构函数的。 上面的例子中,如果我们有一种好的方法能够判断一个指针是否可用,我们就能该对象的状态了。 在面向对象中,对象的关系主要有3种: 组合 聚合 关联 其中组合两者的生命周期一致,不存在什么问题;关联是一种比较弱的关系,形式就是在函数中调用了另外一个对象的方法,此时我们就无法知道该对象是否还存活;聚合关系也是,因为不拥有成员对象的生命周期,所以也无法判断是否存活。 所以上面这种问题要想解决,我们还是得从对象指针入手,我们需要

深入理解C++定位new

匿名 (未验证) 提交于 2019-12-03 00:15:02
简介 一般的new运算符负责在heap堆中找到一个足以能够满足要求的内存块。 new运算符还有另一种变体:定位new运算符(placement new),它能够让程序员指定要使用的位置。既将new运算符用于提供了的地址。 定位new运算符在头文件中。 定位new运算符直接使用传递给它的地址,它不负责判断哪些内存单元已被使用,也不查找未使用的内存块。这将一些内存管理的负担交给了程序员。 下面用一个简单的程序来说明定位new的用法: # include <new> # include <iostream> # include <stdlib.h> using namespace std ; int main ( ) { //chunk of memory内存池 char * buffer = ( char * ) malloc ( 512 * sizeof ( char ) ) ; int * p1 , * p2 , * p3 , * p4 ; //常规new: p1 = new int [ 10 ] ; //定位new: p2 = new ( buffer ) int [ 10 ] ; for ( int i = 0 ; i < 10 ; ++ i ) p1 [ i ] = p2 [ i ] = 20 - i ; cout << "p1 = " << p1 << endl ; /

malloc和new的比较

匿名 (未验证) 提交于 2019-12-03 00:11:01
摘要:本文主要分析了malloc和new两者的区别。 1、申请的内存所处的位置 malloc是c语言中的概念,申请的是堆中的内存空间。堆是计算机操作系统中特殊分配出来的一个内存区域,用于程序的内存动态分配。 new是c++中的概念,申请的空间称之为自由存储区。自由存储区是针对于new的一个概念,可以理解为凡是用new申请的空间,都可以称之为自由存储区。 注意:自由存储区不一定是堆,也有可能是静态存储区。 2、返回类型 new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。 malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。 3、内存分配失败的返回值 new内存分配失败时,会抛出bac_alloc异常,它不会返回NULL; malloc分配内存失败时返回NULL。 4、是否需要指定申请内存的大小 使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。 malloc则需要显式地指出所需内存的尺寸。 5、是否调用析构函数和构造函数 使用new操作符来分配对象内存时会经历三个步骤: 第一步:调用operator new 函数(对于数组是operator new[])分配一块足够大的,原始的,未命名的内存空间以便存储特定类型的对象。 第二步

C++析构函数

匿名 (未验证) 提交于 2019-12-03 00:08:02
背景:用 构造 函数创建对象后,程序负责追踪该对象,直到其过期为止。对象过期时,程序将自动调用一个特殊的成员函数――析构函数 作用: 完成清理工作,所以必须有一个析构函数 ①如果构造函数使用new来分配内存,则析构函数使用delete来释放内存; ②如果构造函数没有使用new,那么析构函数无任务,此时让编译器生成一个什么都不要做的隐式析构函数就行 特点: 如果程序员没有提供析构函数,编译器将隐式地声明一个默认析构函数,并在发现导致对象被删除的代码后,提供默认析构函数的定义。 格式: 在类名前加上~;且与构造函数不同的是,析构函数从来没有参数。 如果程序员要提供,那么析构函数格式如下: ①原型格式:如一个叫Stock的类要写一个析构函数,原型如下: ~Stock(); ②函数格式:因为析构函数不承担任何重要的工作,因此可以将它编写为不执行任何操作的函数 Stock : : ~ Stock ( ) { } 一些细节问题: (1)什么时候应调用析构函数呢? 由编译器决定,通常不要在代码中调用 (2)创建对象的方式不同,析构函数在程序结束是怎么做的? 对存储类不了解的可以看这里 C++存储类 ①如果创建的是静态存储类对象,则其析构函数将在程序结束自动被调用; ②如果创建的是自动存储类对象,则其析构函数将在程序结束时自动被调用; ③如果通过new创建对象,则它将驻留在栈内存或自由存储区中

C++构造函数和析构函数调用虚函数时都不会使用动态联编

匿名 (未验证) 提交于 2019-12-03 00:06:01
可以,虚函数底层实现原理(但是最好不要在构造和析构函数中调用) 可以,但是没有动态绑定的效果,父类构造函数中调用的仍然是父类版本的函数,子类中调用的仍然是子类版本的函数。 effictive c++第九条,绝不在构造和析构过程中调用virtual,因为构造函数中的base的虚函数不会下降到derived上。而是直接调用base类的虚函数。绝不在构造和析构函数中调用virtual函数: a) 如果有继承,构造函数会先调用父类构造函数,而如果构造函数中有虚函数,此时子类还没有构造,所以此时的对象还是父类的,不会触发多态。更容易记的是基类构造期间,virtual函数不是virtual函数。 b) 析构函数也是一样,子类先进行析构,这时,如果有virtual函数的话,子类的内容已经被析构了,C++会视其父类,执行父类的virtual函数。 c) 总之,在构造和析构函数中,不要用虚函数。如果必须用,那么分离出一个Init函数和一个close函数,实现相关功能即可。 原文链接:https://blog.csdn.net/chen134225/article/details/81564972 第一个理由是概念上的。 在概念上,构造函数的工作是生成一个对象。在任何构造函数中,可能只是部分形成对象――我们只能知道基类已被初始化,但并不能知道哪个类是从这个基类继承来的。然而,虚函数在继承层次上是“向前

C++内存管理

匿名 (未验证) 提交于 2019-12-02 23:57:01
new operator 我们平时使用的new是new操作符(new operator),就像sizeof一样是语言内置的,不能改变它的含义,功能也是一样的 比如: string * ps = new string ( "Memory Management" ); 相当于 void * memory = operator new ( sizeof ( string )); // 得到未经处理的内存,为String对象 call string :: string ( "Memory Management" ) on * memory ; // 调构造函数将字符串放到内存 string * ps = static_cast < string *>( memory ); // 使ps指针指向新的对象 new操作符总是做两件事: 1.调用相应的operator new分配内存 2.调用相应的构造函数 如下代码: class T { public : T (){ cout << "构造函数。" << endl ; } ~ T (){ cout << "析构函数。" << endl ; } void * operator new ( size_t sz ){ T * t = ( T *) malloc ( sizeof ( T )); //operator new就是简单的分配内存即可