析构函数

剑指offer学习

女生的网名这么多〃 提交于 2020-03-22 08:26:41
问:C++定义一个空的类型,里面没有任何的成员变量和成员函数。对该类型求sizeof,得到的结果是多少? 答: 是1. 因为空类型的实例中不包含任何信息,本来求sizeof应该是0,但是当我们声明该类型的实例的时候,它必须在内存中占有一定的空间,否则无法使用这些实例。至于占用多少内存,由编译器决定。visual studio 中的每个空类型的实例占用1字节的空间。 如果在该类型中添加一个构造函数和析构函数,求得的sizeof的结果仍是1,因为,调用构造函数和析构函数只需要知道函数的地址即可。而这些函数的地址只与类型相关,而与类型的实例的无关,编译器也不会因为这两个函数而在实例内添加任何额外的信息。 如果把析构函数标记成虚函数的话,C++编译器会为该类型生成虚函数表,并在该类型的每一个实例中添加一个指向虚函数表的指针。 在32位机子上,一个指针占4字节的空间,因此求sizeof得到4; 如果是64位机子,一个指针占8字节的空间,因此求得sizeof得到8; 问: class A { private : int value; public: A (int n) { value =n; } A(A other) { value=other.value ;} void Print( ) {std::cout<<value<<std::endl;} }; int_tmain(int argc

Effective C++ 读书笔记(1-7)

安稳与你 提交于 2020-03-18 07:23:31
作者 Scott Meyers 翻译作者 侯捷 C++ 神牛 台湾人 术语: 1.explicit C++提供了 关键字 explicit,可以阻止不应该允许的经过 转换构造函数 进行的隐式转换的发生。声明为explicit的 构造函数 不能在隐式转换中使用。但他们仍然可以用来进行显示转换。 示例: class Test1 { public: Test1(int n) { num = n; } //普通构造函数 private: int num; }; class Test2 { public: explicit Test2(int n) { num = n; } //explicit(显式)构造函数 private: int num; }; int main() { Test1 t1 = 12; //隐式调用其构造函数, 成功 Test2 t2 = 12; //编译错误,不能隐式调用其构造函数 Test2 t3(12); //显式调用成功 return 0; } 2 undefined behavior int *p=0; //p其实是一个NULL指针 std::cout<<*p;//对一个NULL指针取值会导致不明确行为。 条款一: 封装 encapsulation、继承 inheritance、多态 polymorphism 在STL中,迭代器和函数对象都是在C指针之上塑造的

我是否需要显式调用基本虚拟析构函数?

断了今生、忘了曾经 提交于 2020-03-17 01:18:35
某厂面试归来,发现自己落伍了!>>> 当用C ++覆盖一个类(使用虚拟析构函数)时,我在继承类上再次将析构函数实现为虚拟,但是我是否需要调用基本析构函数? 如果是这样,我想它就是这样...... MyChildClass::~MyChildClass() // virtual in header { // Call to base destructor... this->MyBaseClass::~MyBaseClass(); // Some destructing specific to MyChildClass } 我对吗? #1楼 不,你永远不会调用基类析构函数,它总是像其他人指出的那样自动调用,但这里是结果的概念证明: class base { public: base() { cout << __FUNCTION__ << endl; } ~base() { cout << __FUNCTION__ << endl; } }; class derived : public base { public: derived() { cout << __FUNCTION__ << endl; } ~derived() { cout << __FUNCTION__ << endl; } // adding call to base::~base() here results in

拷贝、赋值与销毁

我只是一个虾纸丫 提交于 2020-03-15 12:21:09
一个类的拷贝控制操作包含: 拷贝构造函数 拷贝赋值运算符 移动构造函数 移动赋值运算符 析构函数 其中: 拷贝和移动构造函数定义了当用同类型的另一个对象初始化本对象时的操作。 拷贝和移动赋值运算符定义了将一个对象赋予同类型的另一个对象时的操作。 析构函数定义了当此类型对象销毁时的操作。 如果一个类没有定义所有这些拷贝控制成员,编译器会自动为它定义缺省的操作。对一些类来说,依赖于这些操作的默认定义会导致灾难。 拷贝构造函数 如果一个构造函数的第一个参数是自身类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数: class Foo{ public: Foo(); //默认构造函数 Foo(const Foo&); //拷贝构造函数 }; 拷贝构造函数的第一个参数必须是引用类型。 虽然可以定义一个接受非 const 引用的拷贝构造函数,但是此参数几乎总是 const 的。 拷贝构造函数在一些情况下会隐式使用,因此拷贝构造函数通常不定义成 explicit 。 合成拷贝构造函数 如果没有自定义拷贝构造函数,编译器会自动生成一个,与合成默认构造函数不同,即使定义了其他构造函数,编译器也会合成一个拷贝构造函数。 一般情况,合成的拷贝构造函数会从给定对象中依次将每个非 static 成员拷贝到正在创建的对象中,每个成员的类型决定了如何拷贝: 对类类型的成员

面向对象(OOP)一

不羁岁月 提交于 2020-03-13 00:25:50
一、面向对象理论   1)面向对象概念     面向对象编程(object Oriented Programming,OOP),是一种计算机编程构架,OOP达到软件工程的三个目标 重用、灵活和扩展性 。   2)什么是对象?     对象是系统中用来描述客观事物的一个实体,包含了若干变量和函数的一个软件集合。客观世界是由对象和对象之间的联系组成。     (万事万物皆对象)   3)什么是类?     类是一组有相同属性和方法的一组对象集合。它属于该类的所有对象提供了统一的抽象描述。其内部包括属性和方法两个主要部分。   4)类和对象的关系     类和对象的关系就好比如模具和铸件的关系。类的实例化结果就是对象,而对一类的对象抽象就是类。类描述了一组有相同特征(属性)和相同行为(方法)的对象。     属性:类的静态描述,相当于变量。     方法:类的动态描述,相当于方法。   5)类的定义 :    class 类名     命名规则:驼峰式命名,首字母大写,如果多个单词,每个单词首字母大写   demo:   class car{     public $color;//颜色,静态 属性,相当于一个变量     //开动,动态,方法,相当于一个函数     public function run(){       echo "开车";     }   }   $car1 =

C#中对象的销毁和内存回收

痞子三分冷 提交于 2020-03-11 18:56:56
在C++中,在堆上分配的内存必须手动回收。即new和delete操作符是要成对使用的。这种方式给程序员提供了强大而灵活的控制能力,程序员可以控制何时销毁对象,回收内存 。这种控制能力,令C++程序员感到自己对程序具有很强的驾驭能力,因此对此功能津津乐道。但是,这种手动回收机制,非常的难以控制,即使经验丰富的C++程序员,也难免疏漏,导致程序中发生内存泄露,轻则导致系统运行越来越慢,重则最终崩溃。这也是手动回收机制所最为人所诟病的地方。 因此,在很多所谓的现代程序语言中,如Java,C#,都提供了自动垃圾回收机制。在对象不再被引用后,系统自动负责销毁对象,回收内存。在托管C++中,通过语言扩展,也可以实现垃圾的自动回收。 自动垃圾回收无需程序员的干预,自动回收废弃对象,释放内存。它有效的避免了内存泄露问题,但是,相应的也带来了新的问题: 1、废弃对象的回收时间是不确定的,即你无法确定系统在何时会执行垃圾回收。每个垃圾收集器都会按照一定的算法执行垃圾收集,但它何时执行,每次执行回收哪些对象是不确定的。 2、它使程序员丧失了部分的控制能力,相应的在系统运行过程中,在性能上也会有一定的损失。 C#中对象的销毁和回收,在实现方式上比较灵活。它既允许立刻销毁对象,回收内存,也允许通过垃圾回收器自动回收。能够做到这点,是因为C#提供了两种对象的销毁方式: 1、类支持IDisposable接口

程序错误、异常(语法、抛出、捕获、传播)、栈展开

别说谁变了你拦得住时间么 提交于 2020-03-11 12:36:33
一、程序错误 编译错误,即语法错误。程序就无法被生成运行代码。 运行时错误 不可预料的逻辑错误 可以预料的运行异常 例如: 动态分配空间时可能不会成功 打开文件可能会失败 除法运算时分母可能为0 整数相乘可能溢出 数组越界…… 二、异常 (一)、异常语法 throw 表达式; try { //try语句块 } catch(类型1 参数1) { //针对类型1的异常处理 } catch (类型2 参数2) { //针对类型2的异常处理 } … catch (类型n 参数n) { //针对类型n的异常处理 } (二)、异常抛出 可以抛出内置类型异常也可以抛出自定义类型异常 throw抛出一个类对象会调用拷贝构造函数 异常发生之前创建的局部对象被销毁,这一过程称为栈展开 (三)、异常捕获 一个异常处理器一般只捕捉一种类型的异常 异常处理器的参数类型和抛出异常的类型相同 …表示可以捕获任何异常 C++ Code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 #include

C与C艹的内存管理方式

自古美人都是妖i 提交于 2020-03-10 08:57:12
  C 内存开辟出的空间一般可以分成: 代码段,数据段( 初始化的数据段, 为初始化的数据段BSS ),堆,栈   代码段 :保存程序文本,指令指针EIP就是指向代码段,可读可执行不可写   数据段 :保存初始化的全局变量和静态变量,可读可写不可执行   BSS :未初始化的全局变量和静态变量   堆(Heap) :动态分配内存,向地址增大的方向增长,可读可写可执行     栈(Stack) :存放局部变量,函数参数,当前状态,函数调用信息等, 向地址减小的方向增长 ,非常非常重要,可读可写可执行      最特别的是 栈stack ,它和别人是反着的!在Windows平台上,栈都是从上(高)向下(低)生长的!。   关于函数调用举个例子: int i= 0x22222222; char szTest [] = "aaaa";//a的ASCII码为0x61 func(i, szTest);   当访问进入func函数时,栈中的形式为(左侧地址 右侧数据):   0x0013FCF0 0x0013FCF8   0x0013FCF4 0x22222222   0x0013FCF8 0x61616161 PS: c++中语句解析顺序和函数调用时候顺序一致:由右向左! 比如如下语句,在输入vector为空时会造成 访问越界! void numIslands(vector<vector

new/delete和malloc/free

北城余情 提交于 2020-03-10 08:56:11
首先我们来谈谈malloc与free malloc和free是C函数库提供的两个函数,malloc从堆上(也叫自由存储区)分配一块合适的内存,程序返回一个指向这块内存的地址。free函数把它归还给内存池供以后使用。 注意的是这块内存并没有以任何形式进行初始化,如果进行初始化:要么自己动手进行初始化,要么使用calloc函数。 两个函数原型: void *malloc(size_t size); void free(void *pointer); malloc的参数就是需要分配的内存字节数(注意malloc不能自动计算字节大小,必须直接传字节数),如果内存池的内存满足这个需求,malloc就会返回一个指向被分配的内存块起始位置的指针;如果内存池为空或者不能满足你的分配需求,malloc将返回一个NULL指针( 这点很重要,每个从malloc分配的内存返回的指针用的时候都要检查是否为NULL )。 free函数的参数要么是NULL(不会产生任何效果),要么就是就是先前从malloc返回的值。只有这两种。释放一块内存的一部分是不允许的,动态分配的内存必须整块一起释放,但是有realloc的特性(缩小一块动态分配的内存),可以有效的释放他尾部的部分内存 注意观察的人会发现malloc返回的是一个 void*,那我们在内存上存储整数,浮点数和其他类型的时候该怎么办?

C++面试题

时光怂恿深爱的人放手 提交于 2020-03-08 08:06:32
语言基础类 1. 指针和引用的区别? (1)指针有自己的一块空间,而引用只是一个别名;  (2)使用 sizeof 看一个指针的大小为 4 字节(32位,如果要是64位的话指针为8字节),而引用则是被引用对象的大小。 (3) 引用必须在定义时被初始化,指针不必; (4)不存在指向空值的引用,但存在指向空值的指针。 2.static和 const的用法,(能说出越多越好)(重点) 首先说说const的用法(绝对不能说是常数) 1.限定变量为不可修改。 2.限定成员函数不可以修改任何数据成员。 3.使用const关键字修饰的变量,一定要对变量进行初始化 下面的声明都是什么意思? const int a; a是一个常整型数 int const a; a是一个常整型数 const int *a; a是一个指向常整型数的指针,整型数是不可修改的,但指针可以 int * const a; a为指向整型数的常指针,指针指向的整型数可以修改,但指针是不可修改的 int const * a const; a是一个指向常整型数的常指针,指针指向的整型数是不可修改的,同时指针也是不可修改的 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。