目录
本文章将会可能会涉及到汇编的知识,不过没有关系,我会讲的尽量通俗易懂;
另外本篇文章开始前,建议了解下什么是函数重载,这个概念很简单的--有相同的函数名,但参数列表不相同的函数,就是函数重载;
和类具有相同名称,并且没有返回值类型的函数,就是类的构造函数
概念模糊、直接举例:
#include <stdio.h>
#include <windows.h>
struct Test
{
Test() // 和类具有相同的名、并且没有返回值
{
}
};
int main()
{
return 0;
}
直接先来说特点吧,然后论证:
1、构造函数在定义对象的时候被调用
2、构造函数可以进行函数重载,可以有很多个
3、创建对象时默认调用的是无参构造
证明1:
构造函数在定义对象的时候被调用;
论证如下:
#include <stdio.h>
struct Test
{
Test()
{
printf("你调用了构造函数\n"); // 此处下断点、如果该函数被调用肯定会停下来。
}
};
int main()
{
Test te;
printf("接力\n");
return 0;
}
我们在Test()构造的输出语句上加断点、当程序调用Test的printf肯定会停下来,这个时候我们转到反汇编,单步步过、直到函数返回之后,就能知到刚刚是在哪里调用的构造函数了
vs2010:F7编译、F5调试、ALT+8反汇编:
F10一直运行到返回:
这里编译器做了优化,可以直接看出来是在Test定义对象的时候调用了构造。
证明2:
构造函数可以进行函数重载,可以有很多个;
论证如下:
#include <stdio.h>
#include <Windows.h>
struct Test
{
Test()
{
printf("你调用了构造函数\n"); // 此处下断点、如果该函数被调用肯定会停下来。
}
Test(int a)
{
printf("重载%d\n",a);
}
Test(int a, int b)
{
printf("重载%d\n",a+b);
}
};
int main()
{
Test te;
Test te1(1);
Test te2(1,1); // 注意、调用有参的构造函数时,需要传递参数
system("pause");
return 0;
}
重载了两个,注意:调用有参数的构造时,需要传递参数。
运行可以通过:
证明3:
创建对象时默认调用的是无参构造;
论证如下:
#include <stdio.h>
#include <Windows.h>
struct Test
{
Test(int a)
{
printf("重载%d\n",a);
}
Test(int a, int b)
{
printf("重载%d\n",a+b);
}
};
int main()
{
Test te; // 普通定义对象的方式、不带参数
system("pause");
return 0;
}
首先我们删除无参构造,看看能否编译通过:
不可以
然后删除有参构造:
#include <stdio.h>
#include <Windows.h>
struct Test
{
Test()
{
printf("你调用了构造函数\n"); // 此处下断点、如果该函数被调用肯定会停下来。
}
};
int main()
{
Test te;
system("pause");
return 0;
}
可以
全部都加上:
#include <stdio.h>
#include <Windows.h>
struct Test
{
Test()
{
printf("你调用了构造函数\n"); // 此处下断点、如果该函数被调用肯定会停下来。
}
Test(int a)
{
printf("重载%d\n",a);
}
Test(int a, int b)
{
printf("重载%d\n",a+b);
}
};
int main()
{
Test te;
system("pause");
return 0;
}
运行结果:
这已经证明了,Test te;平常这样定义对象的时候,调用的是无参构造。如果需要调用有参构造,必须传入参数;
这个特点很简单、有参函数肯定要传参嘛,所以定义对象的时候肯定要传入参数啊;
但是这里建议无论什么时候写类,最好还是写上无参构造,哪怕什么都不做也尽量写上,避免不必要的麻烦。
一般用于初始化类的成员
如下:
#include <stdio.h>
#include <Windows.h>
struct Test
{
int x;
int y;
int z;
Test()
{
}
Test(int x,int y,int z) // 构造函数初始化对象
{
this->x = x;
this->y = y;
this->z = z;
}
};
int main()
{
Test te;
Test te1(1,2,3); // 定义对象并调用有参构造进行初始化
printf("%d %d %d\n",te1.x,te1.y,te1.z); // 输出看看是否初始化成功
system("pause");
return 0;
}
运行如下:
初始化成功。
类的构造函数名前加上'~'这个符号,就是类的析构函数
概念模糊、代码如下:
#include <stdio.h>
#include <Windows.h>
struct Test
{
Test()
{
printf("你调用了一次类的构造函数\n");
}
~Test()
{
printf("你调用了一次类的析构函数\n");
}
};
int main()
{
Test te;
// system("pause"); // 这里就不要让程序停下来了,不然析构不了
return 0;
}
~Test(){}就这个样子
依然直接先来说特点,然后论证:
1、析构函数不能重载、不能有参数
2、析构函数在变量声明周期结束时被调用
3、析构函数被调用分两种情况:堆栈中定义的对象、全局区中定义的对象
证明1:
析构函数不能重载、不能有参数;
编译不通过。
既然不能有参数,那重载更不可能了
证明成功。
证明2:
析构函数在变量声明周期结束时被调用;
局部变量的生命周期是在一个大括号内,即一个所处块结束。
所以:
#include <stdio.h>
#include <Windows.h>
struct Test
{
Test()
{
printf("你调用了一次类的构造函数\n");
}
~Test()
{
printf("你调用了一次类的析构函数\n");
}
};
int main()
{
{
Test te;
printf("te生命周期即将结束。\n");
} // 析构应该在这里被调用
printf("te生命周期结束。\n");
system("pause");
return 0;
}
运行结果如下:
断点
结果
证明成功。
证明3:
析构函数被调用分两种情况:堆栈中定义的对象、全局区中定义的对象;
已知堆栈中定义的对象(局部变量)在块语句结束之后就会被调用,那么带有return的main函数是在返回前调用析构,还是返回后呢?
代码如下:
#include <stdio.h>
#include <Windows.h>
struct Test
{
Test()
{
printf("你调用了一次类的构造函数\n");
}
~Test()
{
printf("你调用了一次类的析构函数\n"); // 断点--汇编
}
};
int main()
{
Test te;
// system("pause"); // 不要使用pause,不然无法返回
return 0;
}
断点-调试-汇编:
可以看到是在函数返回前被调用的。
如果在全局区定义的对象呢?
这个问题很难说,像我一样定义两个断点就行了,如下:
#include <stdio.h>
#include <Windows.h>
struct Test
{
Test()
{
printf("你调用了一次类的构造函数\n");
}
~Test() // 断点
{
printf("你调用了一次类的析构函数\n");
}
};
Test te;
int main()
{
// system("pause"); // 不要使用pause,不然无法返回
return 0; // 断点
}
运行:
发现一运行就断在了return,当我们发F10继续运行的时候,并没有直接调用析构,而是到了括号那里:
继续F10:
通过翻译,可以得知这就是进程结束时的一些收尾工作。
继续F10:
现在大概可以总结了,类的对象定义为全局变量时,是在main函数结束之后进程退出之前,调用的析构函数。
当类的对象定义为局部变量时(堆栈),定义这个对象的块作用域结束时就会调用该对象的析构函数,如果在main函数这个块作用域中定义的对象,那么就是在return之前调用析构。
当类的对象定义为全局变量时(全局区),会在main函数return函数返回之后和进程结束之前,调用该对象的析构函数。
我们知道了析构函数都是在类的对象生命周期结束时被调用,那么就代表下面不会再使用到这个对象;所以析构函数一般用于一些收尾的工作,以防忘记。
比如当你使用了该对象的成员申请了内存(malloc、new等)、或者open了一些文件,那么可以在析构函数中free delete 或者close。
例如:
#include <stdio.h>
#include <Windows.h>
struct Test
{
int x;
char* name;
Test()
{
name = (char*)malloc(sizeof(char)*20); // 构造时动态申请
}
~Test()
{
if(this->name!=0) // 析构时判断是个否为空,不为空释放
{
free(name);
name = 0;
}
}
};
int main()
{
Test te;
return 0;
}
这里我就不运行了,大家可以自己测试下。
构造函数
1、和类具有相同名称,并且没有返回值类型的函数,就是类的构造函数
2、构造函数在定义对象的时候被调用
3、构造函数可以进行函数重载,可以有很多个
4、创建对象时默认调用的是无参构造
析构函数
1、类的构造函数名前加上'~'这个符号,就是类的析构函数
2、析构函数不能重载、不能有参数
3、析构函数在变量声明周期结束时被调用
4、析构函数被调用分两种情况:堆栈中定义的对象、全局区中定义的对象
作者水平不高、如有错还望指出;如有听不懂的地方,可以私信或评论,之后会更改
文章浏览阅读4.3k次,点赞2次,收藏8次。bug的生命周期BUG的生命周期,就是一个BUG被发现到这个BUG被关闭的过程。生命周期中缺陷状态:新建-->指派-->已解决-->待验-->关闭发现BUG-->提交BUG-->指派BUG-->研发确认BUG-->研发去修复BUG-->回归验证BUG-->是否通过验证-->关闭BUG如果待验的BUG在验证时没有解决好,我们需要重新打开--指派—已解决—待验,循环这个过程。中间其他状态:拒绝、延期等_bug生命周期流程图
文章浏览阅读5.3k次,点赞3次,收藏18次。Pandas对行/列求和_pandas对具体列求和
文章浏览阅读1.9w次,点赞32次,收藏228次。做个小结。_visio画用例图
文章浏览阅读2.2k次。在网上看到的恶搞程序员的图片,实在是搞笑。看一次笑一次!程序猿的十年他不是乞丐,请尊称他为程序猿。对,他就是程序猿!其实,你们看到的不是僵尸,他们都是苦逼的程序猿!这不是恶搞的,是一个真实的故事!看他的拐杖,其实也挺可怜的!不知是不是敲代码敲多了,把脚敲颓了!_程序员的道路
文章浏览阅读42次。NetworkingCalling Web Service Methods from Script 为了简化Web Services方法调用,客户端代理的设计被改变了,它在方法调用和回调函数设置方面提供了强大的灵活性。 下面的例子展示了CTP版本中Web Services方法的客户端调用,以及回调函数的使用方式。第一个例子展示了在CTP版本中Web ..._.net6 applicationservices
文章浏览阅读1.1w次,点赞42次,收藏178次。Socket(套接字)是计算机网络编程中的一种抽象概念,它提供了在网络上进行通信的接口。通过使用 Socket,可以在不同计算机之间建立连接,并进行数据的传输和交换。通过 Socket,客户端可以与服务器建立连接并发送请求,服务器接收请求并返回响应。通过 Socket,可以在多个用户之间实现实时的文字、音频或视频通信。可以使用 Socket 在不同计算机之间传输文件,如上传和下载文件。可通过 Socket 在远程计算机上执行指令或操作。_socket编程
文章浏览阅读392次。对于二叉排序树,下面的说法 ___是正确的。(华南理工大学 2006年)设备驱动程序在读写磁盘数据时一般釆用下列哪种I/O方式?(中国科学院大学 2018)下列___交换技术是独占信道工作方式。(中南大学 2006年)命中率高且电路实现简单的Cache 与内存映射方式是___映射方式。(中国科学院大学 2015)_命中率高且电路实现简单的cache内存映射方式
文章浏览阅读3.2k次。可能是没有异常处理,只需要加上throws Exception就解决问题了。_unhandled exceptions: java.lang.instantiationexception, java.lang.illegalacc
文章浏览阅读1.5k次。webView.setWebViewClient(new WebViewClient(){ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { try{ if(url.startsWith("baidumap://")){ Intent _error_unsupported_scheme
文章浏览阅读66次。Embed (一)、基本语法: embed src=url 说明:embed可以用来插入各种多媒体,格式可以是 Midi、Wav、AIFF、AU、MP3等等, Netscape及新版的IE 都支持。url为音频或视频文件及其路径,可以是相对路径或绝对路径。 示例:<embed src="your.mid"> (二)、属性设置: 1、自动播放...
文章浏览阅读330次。PHP -- 文件包含、文件上传漏洞PHP -- 文件包含、文件上传漏洞文件包含文件引入漏洞,是由一个动态页面编译时引入另一个文件的操作。文件引入本身是没有问题,它是用于统一页面风格,减少代码冗余的一种技术。但是在特定的场景下就会形成漏洞jsp:include指令和include动作,其中include动作用于引入动态文件php:include(),include_once(),require()..._php文件上传4漏洞
文章浏览阅读406次。SSL 是需要申请证书的,key和PEM文件要放到服务器路径。然后NGINX下要进行443端口和80端口的绑定。server { listen 80; server_name ietaiji.com www.ietaiji.com; root "D:/aaa/WWW/ietaiji"; index index.html_nginx 和 http无法同时启动