析构函数

条款07:为多态基类声明virtual析构函数

試著忘記壹切 提交于 2019-12-02 08:06:07
Declare destructors virtual in polymorphic base classes. 有许多种做法可以记录时间,因此,设计一个TimeKeeper base class和一些derived classes作为不同的计时方法,相当合情合理: class TimeKeeper { public: TimeKeeper(); ~TimeKeeper(); }; class AtomicClock:public TimeKeeper {...};//原子钟 class WaterClock:public TimeKeeper {...};//水钟 class WristWatch:public TimeKeeper {...} //腕表 许多客户只想在程序中使用时间,不想操心时间如何计算等细节,这时候我们可以设计factory(工厂)函数,返回指针指向一个计时对象。Factory函数会“返回一个base class指针,指向新生成的derived class对象”:TimeKeeper *getTimeKeeper(); 为遵守factory函数的规范,被getTimeKeeper()返回的对象必须位于heap。因此为了避免泄露内存和其他资源,将factory函数返回的每一个对象适当的delete掉很重要: TimeKeeper* ptk =

类的多态

此生再无相见时 提交于 2019-12-02 06:47:59
运算符重载 不能重载的运算符 类属关系运算符“.” 成员指针运算符“.*” 作用域分辨符“::” 三目运算符“?:” 重载运算符 重载运算符函数必须要有重载的类在参数里面 成员重载运算符 调用时,必须是类对象进行调用,且会将自己自动传入做 this 双目运算符操作时,该类对象必须出现在左边,以进行调用 单目运算符操作时,int有是后置++/--,无是前置++/-- 非静态成员 #include using namespace std; class Clock { public: Clock(int hour = 0, int minute = 0, int second = 0); void showTime() const; Clock &operator++(); //前置单目运算符 Clock operator++(int); //int用于区分前置还是后置运算符 后置单目运算符 private: int hour, minute, second; }; void Clock::showTime() const{ cout 非成员运算符 用来解决调用对象不是类对象的情况(但参数还是要有类对象存在) 非成员 #include using namespace std; //非成员函数解决"复数+类"的情况,成员的运算符重载只能做到第一个参数是类类型的 class Complex

C++分区

若如初见. 提交于 2019-12-02 06:01:59
1. C++ 内存布局分为几个区域,各自具备什么特点? 分为: 栈区:由编译器自动分配和释放,存放函数的参数和局部变量的值等。 堆区:由程序员分配和释放,若程序员不释放可能造成内存泄漏。 全局静态区:用来放置全局变量和静态变量,在程序编译时分配。 文字常量区:用来放置常量字符串。 程序代码区:用来放置函数体包括类的成员函数的二进制代码。 2. 当定义类时,编译器会为类自动生成哪些函数? 这些函数各自都有什么特点? 会生成默认构造函数、默认析构函数、缺省的复制构造函数和缺省的赋值运算符重载函数 构造函数特点: 一旦程序员为类定义了一个构造函数,编译器便不会为类自动生成缺省构造函数,必须显示定义一个无参构造函数,构造函数支持函数重载。构造函数的初始化顺序只与声明时的顺序有关,而与初始化表达式中的顺序无关。 析构函数特点: 与类同名,之前冠以波浪号,以区别于构造函数。析构函数没有返回类型,也不能指定参数,因此析构函数只能有一个,不能被重载。缺省的析构函数是个空的函数体,只清除类的数据成员的空间,但对类的成员变量通过new 和 malloc 动态申请的内存无能为力,因此,对于动态申请的内存,应在类的析构函数中通过 delete 或 free 进行释放,否则会造成内存泄漏。 复制构造函数特点: 复制构造函数可以看成是一种特殊的构造函数,创建对象时,只有一个构造函数会被系统自动调用

Keithee Explorer

谁都会走 提交于 2019-12-01 21:54:07
本章学习如何控制类类型对象在拷贝、赋值、移动和销毁时应该做什么。 一个类定义五种特殊的成员函数来控制对象的拷贝、移动、赋值和销毁操作。他们分别是拷贝构造函数、拷贝赋值运算符、移动构造函数、移动赋值函数和析构函数。 拷贝、赋值与销毁 拷贝构造函数 如果构造函数的第一个参数是自身对象的引用,而且其他额外参数都有默认值,则该构造函数是拷贝构造函数。 12345 class {public: Foo(); Foo(const Foo&);} 拷贝构造函数会在几种情况下被隐式地使用,所以不应该是 explicit 。 合成拷贝构造函数 如果我们没有定义一个拷贝构造函数,则编译器会为我们定义一个。编译器将类内的每个非 static 成员拷贝到正在创建的对象中。对于类类型的成员,则使用其拷贝构造函数来拷贝。 123456789 class Sales_data{public: Sales_data(const Sales_data&);private: string bookNo; int units_sold; double revenue;} 现在我们可以理解直接初始化和拷贝初始化的真正区别了,也就是说直接初始化是一个构造函数参数匹配的过程,而拷贝初始化要求我们将右侧的运算对象拷贝到左侧的对象中去,有时候还要求类型转换。 拷贝初始化不仅发生在使用 = 运算符时,还发生在以下情况:

为什么要实现 IDisposable 接口?

≯℡__Kan透↙ 提交于 2019-12-01 18:41:35
一、背景 最近在精读 《CLR Via C#》和 《Effective C#》 的时候,发现的一个问题点。一般来说,我们实现 IDisposable 接口,是为了释放托管资源和非托管资源。不过在 C# 类型定义里面有一个功能类似的东西,那就是 终结器 。 最开始我是学 C++ 的,之后学 C# 的时候发现这玩意儿不论是写法和作用,都跟 C++ 里面的 析构函数 一样。在 C++ 里面的析构函数是在对象释放的时候会被调用,之后这个观点一直被我带到 C#,认为资源释放的动作放在终结器不就行了么。为什么还要我实现 IDisposable 接口,然后让使用者手动释放呢? C++ 版本的析构函数: class Line { public: Line(); ~Line(); private: double length; }; C# 版本的终结器: public class Line { private double _length; public Line() { } ~Line() { } } 二、原因 说起这个原因,首先得从 C# 终结器的 调用时机 说起。终结器的调用是 CLR 在进行 GC 时,如果某个对象写有终结器,即便它应该被释放,也不会马上回收该对象。而 C++ 的析构函数是确定性析构,取决于你调用 delete 的时机。 GC 会将其添加到一个队列当中,单独使用了一个

C#垃圾回收Finalize 和Dispose的理解

╄→尐↘猪︶ㄣ 提交于 2019-12-01 17:36:19
C# 中的析构函数实际上是重写了 System.Object 中的虚方法 Finalize 三种最常的方法如下:   1. 析构函数;(由GC调用,不确定什么时候会调用)   2. 继承IDisposable接口,实现Dispose方法;(可以手动调用。比如数据库的连接,SqlConnection.Dispose(),因为如果及时释放会影响数据库性能。这时候会用到这个,再如:文件的打开,如果不释放会影响其它操作,如删除操作。调用Dispose后这个对象就不能再用了,就等着被GC回收。)   3. 提供Close方法。(类似Dispose但是,当调用完Close方法后,可以通过Open重新打开)   析构函数不能显示调用,而对于后两种方法来说,都需要进行显示调用才能被执行。而Close与Dispose这两种方法的区别在于,调用完了对象的Close方法后,此对象有可能被重新进行使用;而Dispose方法来说,此对象所占有的资源需要被标记为无用了,也就是此对象要被销毁,不能再被使用。例如常见.Net类库中的SqlConnection这个类,当调用完Close方法后,可以通过Open重新打开一个数据库连接,当彻底不用这个对象了就可以调用Dispose方法来标记此对象无用,等待GC回收。明白了这两种方法的意思后,大家在往自己的类中添加的接口时候,不要歪曲了这两者意思。 析构函数

c++语法笔记(下)

我的梦境 提交于 2019-12-01 17:21:25
多态性与虚函数 多态性 (函数重载,运算符重载就是多态性现象) 多态性 :向不同对象发送同一个消息,不同对象在接收时会产生不同的行为。(每个对象用自己的方式去响应共同的消息) 多态性又可以分为静态多态性和动态多态性 静态多态性 在编译时编译系统就可以判定调用哪个重载运算符(函数)。 #include<iostream> using namespace std; class point { public: point(float a, float b) { //构造函数 x = a; y = b; } friend ostream & operator <<(ostream &, point &); //运算符重载 protected: float x, y; }; std::ostream &operator<<(std::ostream &output, point &p) { //运算符重载的定义 output << "(" << p.x << "," << p.y << ")" << endl; return output; } class circle :public point { public: circle(float a, float b, float c) :point(a, b), radius(c) {} //构造函数 friend ostream

对象的构造与析构(二)

◇◆丶佛笑我妖孽 提交于 2019-12-01 07:33:57
对象的构造与析构(二) 目录 1. 析构函数2477203708 2. 对象的构造与析构顺序 多个对象之间 单个对象内部 3. const对象与const成员函数 const对象 const成员函数 4. 成员函数、成员变量与对象的关系 5. 代码实战——数组类IntArray IntArray.h IntArray.cpp IntArray测试 1. 析构函数 C++的类中可以定义一个特殊的清理函数,叫做析构函数,语法规则为 ~ClassName() 析构函数没有参数,也没有返回值类型声明 析构函数在对象销毁时自动被调用 当类中自定义了构造函数,并且构造函数中使用了系统资源(如:堆空间、文件打开,等),则需要自定义析构函数 2. 对象的构造与析构顺序 多个对象之间 多个对象构造时: 栈对象的构造顺序依赖于程序的执行流 堆对象的构造顺序依赖于new的使用顺序 全局对象的构造顺序是不确定的,不同的编译器可能使用不同的规则 多个对象析构时: 栈对象和全局对象的析构顺序与构造顺序相反 堆对象的析构发生取决于delete的使用顺序 单个对象内部 单个对象创建时,对象内部构造函数的调用顺序为: 先调用父类的构造函数 再调用成员变量的构造函数,调用顺序与声明顺序相同 最后调用类自身的构造函数 单个对象内部的析构顺序与构造顺序相反。 3. const对象与const成员函数 const对象

继承(C++语言)

混江龙づ霸主 提交于 2019-12-01 07:20:26
一、继承简介 C++ 中,当定义一个新的类 B 时,如果发现类 B 拥有某个已写好的类 A 的全部特点,此外还有类 A 没有的特点,那么就不必从头重写类 B,而是可以把类 A 作为一个“基类”(也称“父类”),把类 B 写为基类 A 的一个“派生类”(也称“子类”)。这样,就可以说从类 A “派生”出了类 B,也可以说类 B “继承”了类 A。继承是一种封装模型之间关系的抽象,是不同封装模型的层次分类。 一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数。定义一个派生类,我们使用一个类派生列表来指定基类。类派生列表以一个或多个基类命名,形式如下: class derived-class: access-specifier base-class 继承关系的特点: A、子类拥有父类的所有属性和行为 B、子类也是一种特殊的父类 C、子类对象可以当父类对象使用 D、子类中可以添加父类没有的属性和方法 E、子类对象可以直接初始化为父类对象 F、子类对象可以直接赋值给父类对象 G、继承是面向对象编程中代码复用的重要手段 继承举例: #include <iostream> using namespace std; class Parent { public: Parent(int i = 0) { member = i; } void method() { cout <<

STL源码剖析——空间配置器Allocator#1 构造与析构

不羁的心 提交于 2019-12-01 07:11:15
  以STL的运用角度而言,空间配置器是最不需要介绍的东西,因为它扮演的是幕后的角色,隐藏在一切容器的背后默默工作。但以STL的实现角度而言,最应该首先介绍的就是空间配置器,因为这是这是容器展开一切运作的基石。空间配置器顾名思义就是配置空间的器件,为存放在容器里的信息找到安家落户的地方(内存)。   SGI STL上有两个空间配置器,一个是std::allocator,一个是std::alloc,前者只是单纯的把基层的内存配置/释放行为(::operator new 和 ::operator delete)做了一层简单的封装,不作总结;后者才是我们应该重点学习的对象。   一般而言,C++的内存配置与释放操作是这样的: 1 class Foo {...}; 2 Foo* pf = new Foo; 3 delete pf;   其中的new包含两阶段的操作:(1)调用::operator new分配内存;(2)调用Foo::Foo()构造对象内容。delete也包含两阶段的操作:(1)调用Foo::~Foo()将对象析构;(2)调用::operator delete释放内存。。   而在std::alloc中同理,内存配置操作由alloc::allocate()负责,释放由alloc::deallocate()负责;对象构造操作由::construct()负责,析构由: