浅谈C++类(7)--析构函数

欢迎转载,但请标明作者 “九天雁翎”,当然,你给出这个帖子的链接更好。

 不知不觉我都写了6讲了,的确这样讲出来的学习才能迫使我真的去调试每个书上出现的代码,去想些自己能讲出什么新的书上没有的东西,这才是真的学习吧,以前看完书,做道题式的就以为自己基本都掌握了,在类这里好像行不通,因为我的C基础不适合这里。。。。呵呵不说题外话了。这次讲析构函数,相对于构造函数。析构函数就是在类的声明周期结束的时候运行的函数,一般用来把一个类的资源释放出来的家伙。就我了解,JAVA好像不需要这样的工作,他们有垃圾回收器,我看一个比较理性的程序员评价这种情况是有利有弊,类似的简化让JAVA成为了最佳商业模式开发软件,但是让JAVA程序员太脱离底层,让他们的知识匮乏,因为编JAVA不需要那么多知识,而且让JAVA失去了很多底层应用。另外这样的垃圾回收是耗资源的,当时Bjarne Strooustrup就考虑过也给C++加一个这样的特性,但他又说,作为一个系统开发级及常用来开发驱动程序的语言,他无法接受那样的效率损失。所以最后C++没有加上这个特性。又讲多了,看析构函数吧。

例7.0:

#include <string>
#include <iostream>
using namespace std;
bool being = true;                 //定义一个全局bool变量
class Fruit               //定义一个类,名字叫Fruit
{
 string name;     //定义一个name成员          
 string colour;   //定义一个colour成员
public:
 void print()              //定义一个输出名字的成员print()
 {
  cout<<colour<<" "<<name<<endl;
 }
 Fruit(const string &nst = "apple",const string &cst = "green"):name(nst),colour(cst)
 {
 being = true;                          //表示这个东西存在
 }  //构造函数
 ~Fruit()                        //这就是传说中的析构函数
 {
 being = false;                      //表示他不存在了
 } 
};
int main()
{
 {
  Fruit apple("apple");  //定义一个Fruit类对象apple
  cout <<"apple being?: "<<being<<endl;             
 }
 cout <<"apple being?: "<<being<<endl;
 return 0;
}

首先看到不要惊讶 :),我们的构造函数和析构函数都作了些什么啊。我说过构造函数就是构造一个类对象会运行的函数,析构函数就是类生命周期结束时运行时运行的函数,不仅仅是我们的一般理解啊,从逻辑上来讲,他们可以DO Everything,你首先要知道他们能干什么啊:)而且还要知道他们什么时候起作用,因为我们用一个大括号把apple的定义括起来了,在大括号消失的时候,apple就需要消失了,于是这时候调用了析构函数。下面我们先看看可以做什么的例子。

例7.1:

#include <string>
#include <iostream>
using namespace std;
bool being = true;
class Fruit               //定义一个类,名字叫Fruit
{
 string name;     //定义一个name成员          
 string colour;   //定义一个colour成员
public:
 void print()              //定义一个输出名字的成员print()
 {
  cout<<colour<<" "<<name<<endl;
 }
 Fruit(const string &nst = "apple",const string &cst = "green"):name(nst),colour(cst)
 {
 being = true;
 cout <<"Aha,I'm "<<name<<". I have created!"<<endl;
 }  //构造函数
 ~Fruit()
 {
 being = false;
 cout <<"Dame it!"<<"I'm "<<name<<". And who killed me?"<<endl;
 } 
};
int main()
{
 {
  Fruit apple("apple");  //定义一个Fruit类对象apple
  cout <<"apple being?: "<<being<<endl;
 }
 cout <<"apple being?: "<<being<<endl;
 return 0;
}

你运行看看,就知道了:)在一个对象定义的时候他会高呼自己被创造了,当他消失的时候他会宣布自己的死亡:)好的,Fruit的对象看起来已经知道自己什么时候有生命了,让我们来看看到底什么时候吧。

例7.2:

#include <string>
#include <iostream>
using namespace std;
bool being = true;
class Fruit               //定义一个类,名字叫Fruit
{
 string name;     //定义一个name成员          
 string colour;   //定义一个colour成员
public:
 void print()              //定义一个输出名字的成员print()
 {
  cout<<colour<<" "<<name<<endl;
 }
 Fruit(const string &nst = "apple",const string &cst = "green"):name(nst),colour(cst)
 {
 being = true;
 cout <<"Aha,I'm "<<name<<". I have created!"<<endl;
 }  //构造函数
 ~Fruit()
 {
 being = false;
 cout <<"Dame it!"<<"I'm "<<name<<". And who killed me?"<<endl;
 } 
};
Fruit banana("banana");
void Fb()
{
 cout<<"我是一个函数的开始"<<endl;
 Fruit pear("pear");
 cout<<"我是一个函数的结束"<<endl;
}

int main()
{
 cout<<"我是程序的开始"<<endl;
 Fb();
 cout<<"我是for循环的开始"<<endl;
 for(bool bi = true;bi;bi=false)
 {
 Fruit orange("orange");
 cout<<"我是for循环的结束"<<endl;
 }
 {
  cout<<"我是语句块的开始"<<endl;
  Fruit apple;  //第一种情况,语句块中创建。
  cout<<"我是语句块的结束"<<endl;
 }
 cout<<"我是程序的结束"<<endl;
 return 0;
}

就这个程序运行的情况来看,一个类的生命周期和一个普通变量的生命周期类似,全局变量最先创建,程序结束时结束,函数体内的变量调用时创建,函数结束时结束,for循环内的变量在for循环结束时结束,语句块内的变量在语句块结束时结束。本来Bjarne stroustrup就宣称他希望让类就像内置类型一样使用,看来他不是说着好玩的:)这里要说明的是,即使你没有定义析构函数,系统也会像定义默认构造函数一样帮你定义一个。让我们看看什么时候需要析构函数呢?

例7.3:

#include <string>
#include <iostream>
using namespace std;
class Fruit               //定义一个类,名字叫Fruit
{
 string name;     //定义一个name成员          
 string colour;   //定义一个colour成员
public:
 void print()              //定义一个输出名字的成员print()
 {
  cout<<colour<<" "<<name<<endl;
 }
 Fruit(const string &nst = "apple",const string &cst = "green"):name(nst),colour(cst)
 {
 cout <<"Aha,I'm "<<name<<". I have created!"<<endl;
 }  //构造函数
 Fruit(Fruit &aF)           //还记得我吗?我是复制构造函数
 {
  name = "another " +aF.name;             
 }
 ~Fruit()
 {
 cout <<"Dame it!"<<"I'm "<<name<<". And who killed me?"<<endl;
 } 
};

int main()
{
 cout<<"main begin"<<endl;
 cout<<"created *P"<<endl; 
 {
  Fruit *p = new Fruit; 
  cout<<"created another apple"<<endl;
  Fruit apple(*p);
 }

 
 cout<<"main end"<<endl;
 return 0;
}

运行这个程序你发现什么了?对,首先,运行复制构造函数就不运行构造函数了,因为another apple没有宣布自己的诞生,其次,当语句块消失的时候another apple自动调用了析构函数,他宣布他“死”了,但是动态创建的由*p指向的对象虽然宣布自己诞生了,但是却重来没有宣布自己死过,哪怕程序结束了也是这样!!不知道vc有没有回收内存的措施,不然我甚至怀疑你要是重复调试这个程序,可以使你的机子崩溃,当然,假如可以的话将不知道需要多少次,但是理论上确实可以的。这就是内存泄露!作为一个C++程序员,需要了解的东西比一个JAVA程序员要多的多,回报是你能做的事情也多地多!这就是你需要记住的一个,动态创建的对象,记得手动把它撤销。就像下面的例子一样。

例7.4:

#include <string>
#include <iostream>
using namespace std;
class Fruit               //定义一个类,名字叫Fruit
{
 string name;     //定义一个name成员          
 string colour;   //定义一个colour成员
public:
 void print()              //定义一个输出名字的成员print()
 {
  cout<<colour<<" "<<name<<endl;
 }
 Fruit(const string &nst = "apple",const string &cst = "green"):name(nst),colour(cst)
 {
 cout <<"Aha,I'm "<<name<<". I have created!"<<endl;
 }  //构造函数
 Fruit(Fruit &aF)           //还记得我吗?我是复制构造函数
 {
  name = "another " +aF.name;             
 }
 ~Fruit()
 {
 cout <<"Dame it!"<<"I'm "<<name<<". And who killed me?"<<endl;
 } 
};

int main()
{
 cout<<"main begin"<<endl;
 cout<<"created *P"<<endl; 
 {
        Fruit *p = new Fruit; 
  cout<<"created another apple"<<endl;
  Fruit apple(*p);
  cout<<"delete p"<<endl;
  delete p;
 }

 
 cout<<"main end"<<endl;
 return 0;
}

这样才能保证你的机子不会崩溃。当你删除指针的时候系统帮你自动调用了对象的析构函数,假如上面的例子还不能摧毁你对自己自己内存足够大的信心的话,看下面的例子;

例7.5:

#include <string>
#include <iostream>
using namespace std;
class Fruit               //定义一个类,名字叫Fruit
{
 string name;     //定义一个name成员          
 string colour;   //定义一个colour成员
public:
 void print()              //定义一个输出名字的成员print()
 {
  cout<<colour<<" "<<name<<endl;
 }
 Fruit(const string &nst = "apple",const string &cst = "green"):name(nst),colour(cst)
 {
 cout <<"Aha,I'm "<<name<<". I have created!"<<endl;
 }  //构造函数
 Fruit(Fruit &aF)           //还记得我吗?我是复制构造函数
 {
  name = "another " +aF.name;             
 }
 ~Fruit()
 {
 cout <<"Dame it!"<<"I'm "<<name<<". And who killed me?"<<endl;
 } 
};

int main()
{
 cout<<"main begin"<<endl;
 cout<<"created *P"<<endl; 
 {
        Fruit *p = new Fruit[10]; 
  cout<<"created another apple"<<endl;
  Fruit apple(*p);
  cout<<"delete p"<<endl;
  delete []p;
 }

 
 cout<<"main end"<<endl;
 return 0;
}

你会发现创建一个对象的数组时,分别为每一个调用了构造函数,删除一个动态数组对象的时候系统帮你自动为每一个调用了析构函数,还不了赖嘛,但是别忘了p前面的[]表示这是个数组,更别忘了删除它,你可以把10改成更大的数并不删除它来尝试一下,呵呵。析构函数就讲到这里罗。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/293702.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Hadoop示例程序WordCount详解及实例

2019独角兽企业重金招聘Python工程师标准>>> 1.图解MapReduce MapReduce整体流程图 并行读取文本中的内容&#xff0c;然后进行MapReduce操作 Map过程&#xff1a;并行读取三行&#xff0c;对读取的单词进行map操作&#xff0c;每个词都以形式生成 reduce操作是对ma…

《SAS编程与数据挖掘商业案例》学习笔记之十

继续之前的读书笔记&#xff0c;本次讲解sas主要的变量操作&#xff0c;包括基本赋值语句、累加语句、keep语句、retain语句、array语句、rename语句、length语句。 1.基本赋值语句 zx y; zsum(x,y); sum函数的好处是可以克服缺失值的影响&#xff1b; 2.如果表达式中既有数值…

title与h1标签的区别和联系

很多新站长在网站SEO过程中&#xff0c;会认为把H1等同于Title。 其实两是有区别和联系的&#xff0c;两者不能划等号。下面主要从文章和页面角度分析title和H1。 H1等同于title吗? H1不等于title。H1&#xff0c;大标题。一般出现在文章页面&#xff0c;作用如同一张报…

浅谈C++类(5)--友元

欢迎转载&#xff0c;但请标明作者 “九天雁翎”&#xff0c;当然&#xff0c;你给出这个帖子的链接更好。 呵呵&#xff0c;又来了&#xff0c;自从我开始尝试描述类以来&#xff0c;我发现我自己是开始真的了解类了&#xff0c;虽然还不到就明白什么叫oo的高深境界&#xff0…

.NET 云原生架构师训练营(系统架构)--学习笔记

▲ 点击上方“DotNet NB”关注公众号回复“1”获取开发者路线图学习分享 丨作者 / 郑 子 铭 这是DotNet NB 公众号的第176篇原创文章目录对外展现的功能内部功能功能交互与价值通路系统架构目标认识系统的价值通路认识功能架构&#xff0c;通过把功能结构与形式结构结合来描…

JFinal整合CKFinder

2019独角兽企业重金招聘Python工程师标准>>> 使用JFinal对之前的系统进行重构&#xff0c;老系统使用了CKEditorCKFinder编辑器&#xff0c;发现在web.xml中配置ConnectorServlet会被JFinalFilter过滤处理&#xff0c;而且使用JFinal之后也不想在web.xml中增加额外的…

通过简单的Word Count讲解MapReduce原理以及Java实现

MapReduce原理&#xff1a; MapReduce采用"分而治之"的思想&#xff0c;把对大规模数据集的操作&#xff0c;分发给一个主节点管理下的各个分节点共同完成&#xff0c;然后通过整合各个节点的中间结果&#xff0c;得到最终结果。简单地说&#xff0c;MapReduce就是&q…

经验总结03-dwr

java使用dwr进行ajax请求。 1.编写对应的查询数据的类。 2.添加dwr.jar&#xff0c;可直接复制到lib下。 3.在dwr.xml配置对应的类&#xff0c;让其可在页面中调用类。<create creator"new" javascript"test"><param name"class" value…

浅谈C++类(6)--复制构造函数

欢迎转载&#xff0c;但请标明作者 “九天雁翎”&#xff0c;当然&#xff0c;你给出这个帖子的链接更好。 还记得&#xff08;1&#xff09;中讲到的构造函数吗&#xff1f;复习一下&#xff0c;这次我们重载一个新的默认构造函数--即当你不给出初始值时调用的构造函数&#x…

.NET 6新特性试用 | TryGetNonEnumeratedCount

前言.NET 6新增了TryGetNonEnumeratedCount方法&#xff0c;计算可枚举类型的元素总数。LINQ不是已经有了Count方法吗&#xff0c;为什么还要画蛇添足呢&#xff1f;Demo尝试下列代码&#xff1a;var b new B<int>(); Console.WriteLine($"{b.Count()}");var …

阻塞队列之七:DelayQueue延时队列

一、DelayQueue简介 是一个无界的BlockingQueue&#xff0c;用于放置实现了Delayed接口的对象&#xff0c;其中的对象只能在其到期时才能从队列中取走。这种队列是有序的&#xff08;PriorityQueue实际存放Delayed接口对象&#xff09;&#xff0c;即队头对象的延迟到期时间最短…

研究表明:喝酒“上脸”是基因突变,不仅容易老年痴呆,还容易得胃癌

全世界只有3.14 % 的人关注了爆炸吧知识本文转自科研大匠“喝酒上脸的人能喝&#xff01;”这句话&#xff0c;不管来自天南还是海北的&#xff0c;在酒桌上&#xff0c;肯定都耳熟能详有没有&#xff1f;其实&#xff0c;喝酒“上脸”并不意味着能喝&#xff0c;而是一种基因突…

PHP中如何配置smarty框架实现PHP代码和HTML代码分离

header(Cache-Control:Private);//保留用户填写的信息 session_start();//开启缓存 define(MYCMS,UTF-8);//定义网站编码常量 define(ROOT,str_replace(\\,/,realpath(dirname((__FILE__))./../)));//定义根目录常量 ../是返回上级目录 define(TPL,ROOT./tpl);//定义网页模板的…

Hook API (C++)

一、基本概念&#xff1a; 钩子(Hook)&#xff0c;是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息&#xff0c;而且 所监视的窗口可以是其他进程所创建的。当消息到达后&#xff0c;在目标窗口处理函数之前处理它。钩子机制允许应用程序…

本科、硕士、博士的区别(终极版)

全世界只有3.14 % 的人关注了爆炸吧知识本文转自募格学术本科生和研究生到底有何区别&#xff1f;硕士和博士又有什么不同&#xff1f;这是很多人都有的困惑&#xff0c;对于这个问题的说法也有很多版本&#xff0c;我们挑选了几个比较经典的版本&#xff0c;以期能和大家一同探…

C# Jpush 极光推送消息推送

简介消息推送&#xff08;Push&#xff09;指运营人员通过自己的产品或第三方工具对用户移动设备进行的主动消息推送。用户可以在移动设备锁定屏幕和通知栏看到push消息通知&#xff0c;通知栏点击可唤起APP并去往相应页面。我们平时在锁屏上看到的微信消息等等都属于APP消息推…

Linux 环境变量 $PATH

我们知道查阅文件属性的指令 ls 完整文件名为&#xff1a;/bin/ls(这是绝对路径)&#xff0c;那为什么可以在任何地方执行/bin/ls 这个指令呢&#xff1f; 为什么在任何目录下输入 ls 就一定可以显示出一些讯息而不会说找不到该 /bin/ls 指令呢&#xff1f; 这是因为环境变量 …

文件项目SVN+TortoiseSVN+Subclipse使用总结

近来使用开辟的过程中涌现了一个小问题&#xff0c;顺便记录一下原因和方法--文件项目 一、SVN、TortoiseSVN、Subclipse分析 团队开辟技术&#xff1a; (1)单元测试&#xff1b;(2)版本控制&#xff1b; (3)项目主动化&#xff1b; SCM:软件配置管理&#xff0c;包含SVN&#…