析构函数

虚函数与构造函数和析构函数之间的问题

99封情书 提交于 2019-11-28 11:03:06
多态中为什么要将析构函数定义为虚函数 构造函数不能被定义为虚函数,在多态中,析构函数最好定义成虚函数 为什么构造函数不能被定义成虚函数?   构造一个对象时,必须要知道对象的类型,而虚函数本身是在运行期间确定其类型的。   虚函数执行依靠虚函数表,而虚函数表是在构造函数中进行初始化的。在构造对象期间,虚函数表未被初始化,将无法进行。 析构函数定义为虚函数的原因。   析构函数的作用是将类的对象从内存中抹掉。在多态情况中,考虑以下两种情况:   1. 用子类指针绑定子类对象,析构的时候,能正常析构。   2. 用父类指针绑定子类对象,假如父类没有将析构函数定义成虚函数的话,父类指针无法根据虚函数表找到子类的析构函数,所以只能调用父类析构函数对子类析构,从而造成内存泄漏。 来源: https://blog.csdn.net/weixin_41731295/article/details/100056803

C#GC垃圾回收和析构函数和IDisposable的使用

心不动则不痛 提交于 2019-11-28 09:50:06
一,什么是GC 1,GC是垃圾回收器,一般来说系统会自动检测不会使用的对象或变量进行内存的释放,不需要手动调用,用Collect()就是强制进行垃圾回收,使内存得到及时的释放,让程序效率更高. 2,GC:只负责回收托管对象,不负责回收非托管对象。 那什么是垃圾? 垃圾是完全访问不到的东西了,就是当前程序执行后该对象或值没有被引用 如下图: 代码如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TestDemo { public class TestDemo { private static Test t = new Test()//静态的不可能被回收 静态持有的引用也不会被回收 { Id = 123, Name = "Test" }; public static void show() { ///{}这个括号是代码分块的意思,两个块之间是不影响的,第一块执行完后,一般来说就是会被主动GC释放 ///而这里obj没有被释放的原因是因为静态遍历对obj这个引用类型变量的使用 { object obj= new { Name = 1 }; t.obj = obj; int i =

C++——虚析构

末鹿安然 提交于 2019-11-28 08:31:00
目的: //只执行了 父类的析构函数 //向通过父类指针 把 所有的子类对象的析构函数 都执行一遍 //向通过父类指针 释放所有的子类资源 方法:在父类的析构函数前+virtual关键字 #define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; //虚析构函数 class A { public: A() { p = new char[20]; strcpy(p, "obja"); printf("A()\n"); } virtual ~A() { delete [] p; printf("~A()\n"); } protected: private: char *p; }; class B : public A { public: B() { p = new char[20]; strcpy(p, "objb"); printf("B()\n"); } ~B() { delete [] p; printf("~B()\n"); } protected: private: char *p; }; class C : public B { public: C() { p = new char[20]; strcpy(p, "objc"); printf("C()\n"); } ~C() {

C++基础学习5

有些话、适合烂在心里 提交于 2019-11-28 07:12:18
9类和对象进一步 9.1构造函数 如果一个类中所有的成员都是公用的,则可以在定义对象时对数据成员进行初始化。 但是,如果数据成员是私有的,或者类中有private或protected的成员, 就不能用这种方法初始化。 class Time {public : //声明为公用成员 hour; minute; sec; }; Time t1={14,56,30}; //将t1初始化为14:56:30 构造函数的作用 构造函数的名字必须与类名同名,构造函数没有返回值,因此也不需要在定义构造函数时声明类型, 以便编译系统能识别它并把它作为构造函数处理。 在类内定义构造函数,也可以只在类内对构造函数进行声明而在类外定义构造函数。 构造函数不需用户调用,也不能被用户调用。 带参数的构造函数 构造函数首部的一般格式为构造函数名(类型 1 形参1,类型2 形参2, …) 定义对象的一般格式为 类名 对象名(实参1,实参2, …); #include <iostream> using namespace std; class Box {public : Box(int,int,int);//形参由定义对象时给出 int volume( ); private : int height; int width; int length; }; //声明带参数的构造函数//声明计算体积的函数 Box::Box

《effective C++》总结【1】

你。 提交于 2019-11-28 06:17:50
最近开始看c++经典著作《effective c++》,总结了一些要点。 关键字的使用 1 自定义类的构造函数应该加上explicit,目的是为了防止隐式转换。除非有好的理由说明需要隐式转换,否则默认加上explict防止出现没有预料到的情况。 2 对于内置类型(int, double)和STL来说,pass-by-value比pass-by-reference更加高效,但是对于自定义的类,pass-by-reference比pass-by-value更加高效。 3 由于#define定义的宏常量可能没有进入到记号表,导致追踪不到。我是从来没遇到过这种情况,但是作者还是建议在众多场合 不要 使用#define。 定义常量整数或实数时,尽量用const int或const double代替。这会导致更少量的码。 定义常量指针时,要用两次const。const char* authorname = “scott meyers”。 定义类中的常量时,还要用static修饰。 定义类中的数组时,必须要定义常量,这时如果编译器不允许在类内声明时定义常数,就可以用enum代替。例如   class GamePlayer{   private:     enum { NumTurns = 5};     int scores[NumTurns];   }   enum的行为比较像

虚析构函数

落花浮王杯 提交于 2019-11-28 03:19:17
  继承关系对基类拷贝控制最直接的影响是基类通常应该定义一个虚析构函数,这样我们就能动态分配继承体系中的对象了。   如前所述,当我们delete一个动态分配的对象的指针时将执行析构函数。如果该指针指向继承体系中的某个类型,则由可能出现指针的静态类型与被删除的动态类型不符的情况。例如,如果我们delete一个Quote*类型的指针,则该指针有可能实际指向了一个Bulk_quote类型的对象。如果这样的话,编译器就必须清楚它应该执行的是Bulk_quote的析构函数。 和其它函数一样,我们通过在基类中将析构函数定义成虚函数以确保执行正确的析构函数版本 : class Quote { //如果我们删除的是一个指向派生类对象的基类指针,则需要虚析构函数 virtual ~Quote() = default; //动态绑定析构函数 }; 和其它虚函数一样,析构函数的虚属性也会被继承。因此,无论Quote的派生类使用合成的析构函数还是定义自己的析构函数,都将是虚析构函数。只要基类的析构函数是虚函数,就能确保当我们delete基类指针时将运行正确的析构函数版本: 来源: https://www.cnblogs.com/bootblack/p/11388573.html

小结《malloc与new之区别》

北城余情 提交于 2019-11-28 02:11:34
1,malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存 2,对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。 3,因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。 4,C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。 5、new可以认为是malloc加构造函数的执行。new出来的指针是直接带类型信息的。而malloc返回的都是void指针。 CPP代码 class Obj { public : Obj( void ){ cout << “Initialization” << endl; } ~ Obj( void ){ cout << “Destroy” << endl; } void Initialize( void ){ cout << “Initialization” << endl; } void Destroy( void ){

构造函数与析构函数

☆樱花仙子☆ 提交于 2019-11-28 01:25:59
目录 构造函数: 构造函数需要注意的几点: 单参构造与类型转换: 初始化列表: 析构函数: 构造函数: 构造函数是类的一种特殊的成员函数,特点是:会在创造一个新对象时调用执行。 构造函数需要注意的几点: 1、构造函数是可以被重载的,不会有冲突,并且同一个名字的函数可以有多个版本。 Stu(void) { cout << "我是无参构造" << endl; } Stu(const char* name,char sex) { cout << "我是有参构造,我的参数是name,sex" << endl; } Stu(int id,short age) { cout << "我是有参构造,我的参数是id,age" << endl; } 2、缺省构造是编译器自动生成的一个什么都不做的构造函数(唯一的作用就是避免系统错误)。 注意:当类实现一个有参构造后,无参构造就不会再自动生成,如果有需要必须显式的写出来。 3、无参构造未必是真的无参,当给有参构造的所有参数设置默认形参,调用这种构造函数就不需要传参。 单参构造与类型转换: class Test { public: int num; Test(int n) { num = n; } }; int main() { Test t = 20; //这部操作其实只是实现了单参构造,缺会给人一个类型转换的错觉 } 注意:

PHP面向对象学习-属性 类常量 类的自动加载 构造函数和析构函数 访问控制(可见性)

*爱你&永不变心* 提交于 2019-11-28 00:30:01
在类的成员方法里面,可以用 -> (对象运算符): $this->property (其中 property 是该属性名)这种方式来访问非静态属性。静态属性则是用 :: (双冒号): self::$property 来访问。更多静态属性与非静态属性的区别参见 Static 关键字 。 Example #1 属性声明 跟 heredocs 不同, nowdocs 可在任何静态数据上下文中使用,包括属性声明。 Example #2 示例:使用 nowdoc 初始化属性 类常量 可以把在类中始终保持不变的值定义为常量。在定义和使用常量的时候不需要使用 $ 符号。 常量的值必须是一个定值,不能是变量,类属性,数学运算的结果或函数调用。 接口(interface)中也可以定义常量。更多示例见文档中的 接口 部分。 自 PHP 5.3.0 起,可以用一个变量来动态调用类。但该变量的值不能为关键字(如 self , parent 或 static )。 Example #1 定义和使用一个类常量 类的自动加载 在编写面向对象(OOP) 程序时,很多开发者为每个类新建一个 PHP 文件。 这会带来一个烦恼:每个脚本的开头,都需要包含(include)一个长长的列表(每个类都有个文件)。 在 PHP 5 中,已经不再需要这样了。 spl_autoload_register()

重读深度探索C++对象模型:虚函数

走远了吗. 提交于 2019-11-27 21:46:31
1.单一继承下的虚函数(待更新) 2.多重继承下的虚函数 多重继承讲给虚函数带来很大的问题,主要有三点: 1.虚析构函数:    析构函数一般都是虚函数,不然删除某个指向派生类的基类指针将不会析构派生类的内容。 首先关于多重继承: 如以下这个关系: A与B同级,C分别继承这两个类。 如果A* a=new C; B* b=new C; 则通过a只能调用displayA(); 通过b只能调用displayB(); 也就是说b->displayA()这样是非法的。 然后是关于多重继承对虚析构函数的影响。 现在A是基类中最左边的类,如果我们 A* a=new C; 现在a指向的实际上是现在这个对象地址的最起始处,而如果我们 B* b=new C; 现在b指向的起始是类中B subobject处,也就是说使用B类作为基类指针的话指向的不是起始处。 但是当我们调用析构函数时,比如delete b时,此时 应该将b再调整至类的起始地址处 。 在单一继承时候是不存在这个问题的,因为只有一个基类,指针也一直指向类的起始地址,而 当加入多重继承 , 且经由非最左边的类的指针 来delete时, 需要在delete前将指针在调整至类的起始处 ,因为一开始b并不是指向起始,而是指向B subobject处的。 class A { public: virtual ~A() { cout<<"a bybye"