假期间闲来无事,就下载了某大师的VC++视频资料。在讲到C++时,说是如果程序员没有自己定义默认构造函数,那么编译器会自动为我们产生一个默认的构造函数。本来这个错误的认识很多程序员都有,不足为奇。但有这么多年编程经验的高手也有这样的错误认识就不禁让我哑然了。
其实编程语言和我们所用的任何软件没有区别,例如Photoshop、AutoCAD之类。其唯一不同的是我们用的编程语言是基于编译器的,而应用软件是基于我们的编程语言的。
既然我们所用的软件是基于编译器的,那么理解编译器在背后到底为我们做了些什么、在什么情况下做了哪些事情就显得异常重要。这就像Photoshop会为你产生一些基本图形例如矩形、三角形之类,而不会凭空产生一些风景优美的图片一样。
在《C++ Annotated Reference Manual(ARM)[ELLIS90]》中的Section 12.1告诉我们:"Default constructors...在需要的时候被编译器产生出来"。
其实默认构造函数也是分为两类的:有用的、无用的。
所谓有用的标准也是就默认构造函数会为我们的类做一些初始化操作。那么无用的就不会做任何工作,从而对我们的类也就没有任何意义。所以,我们通常所说的默认构造函数是指有用的默认构造函数,其英文名字叫nontrivial default constructor。
那么到底什么时候编译器会为我们产生nontrivial default constructor呢?有下面四中情况:
①如果一个类里面某个成员对象有nontrivial default constructor,编译器就会为我们的类产生nontrivial default constructor。
那么编译器这样做的理由是什么?
答案是因为类成员对象有nontrivial default constructor,那么编译器就需要显式的来调用这个类成员对象的nontrivial default constructor。而编译器想显式的调用类成员对象的nontrivial default constructor,就需要自己来合成一些代码来调用。但是记住,编译器合成的nontrivial default constructor仅仅调用类成员对象的默认构造函数,而不对我们类里面的其它变量做任何初始化操作。
也就是说,如果你想初始化类成员变量以外的变量例如一个int、一个String,那么必须自己定义默认构造函数来完成这些变量的初始化。而编译器会对你定义的默认构造函数做相应的扩展,从而调用类成员对象的nontrivial default constructor。
②如果一个派生类的基类有nontrivial default constructor,那么编译器会为派生类合成一个nontrivial default constructor。
编译器这样的理由是:因为派生类被合成时需要显式调用基类的默认构造函数。
③如何一个类里面隐式的含有任何virtual function table(或vtbl)、pointer member(或vptr)。
编译器这样做的理由很简单:因为这些vtbl或vptr需要编译器隐式(implicit)的合成出来,那么编译器就把合成动作放到了默认构造函数里面。所以编译器必须自己产生一个默认构造函数来完成这些操作。
所以如果你的类里带有任何virtual function,那么编译器会为你合成一个默认构造函数。
④如果一个类虚继承于其它类。
编译器这样做的理由和③类似:因为虚继承需要维护一个类似指针一样,可以动态的决定内存地址的东西(不同编译器对虚继承的实现不仅相同)。
那么除了以上四种情况,编译器并不会为我们的类产生默认构造函数。
所以编程中切忌想当然,要明白哪些事情是编译器做的,哪些事情需要程序员来完成的。就像堆所占用的资源需要程序员自己来释放,而栈空间是编译器管理的一样。
只有如此,才能编写出质量更高的代码。
分享到:
相关推荐
有一些错误的认识关于C++默认构造函数。例如,一些程序员认为,如果程序员没有自己定义默认构造函数,那么编译器将自动提供一个默认的构造函数。这是一个错误的认识。实际上,C++只在某些情况下提供默认构造函数。 ...
本文将深入探讨几个常见的JAVA错误代码及其原因。 首先,我们来看一个涉及到接口和类的示例: ```java interface A { int x = 0; } class B { int x = 1; } class C extends B implements A { public void pX(){ ...
7. **构造函数与析构函数**:解释它们在对象生命周期中的作用,以及如何使用默认构造函数、拷贝构造函数。 8. **运算符重载**:如何根据需要为类的成员定义特定的行为。 9. **模板**:探讨泛型编程,使代码具有更...
C#中,构造函数可以有默认和带参数两种形式,析构函数以`~`符号开头。 7. **异常处理**:C#提供了异常处理机制,通过try-catch-finally语句块捕获和处理运行时可能出现的错误。 8. **集合与泛型**:C#内置了多种...
关于存储程序的语法,有以下几点重要组成部分: - CREATE PROCEDURE 和 CREATE FUNCTION 语句用于创建存储过程和函数。 - ALTER PROCEDURE 和 ALTER FUNCTION 语句用于修改已存在的存储过程和函数。 - DROP ...
总是提供默认构造函数以将结构成员初始化为它们的默认值。在结构中初始化实例字段是错误的。在类中,必须初始化实例对象. 使用 new 运算符创建结构对象时,将创建该结构对象,并且调用适当的构造函数。与类不同的是,...
在分析源码时,我们可以关注以下几个方面: - 如何处理网络接口的发现和选择? - 如何构建和解析DHCP报文,确保它们遵循协议规范? - 如何有效地管理和调度定时器,以满足DHCP交互的时序要求? - 如何设计事件循环...
下面介绍几个成员函数: Add 增加CString RemoveAt 删除指定位置CString对象 RemoveAll 删除数组中所有CString对象 GetAt 得到指定位置的CString对象 SetAt 修改指定位置的CString对象 InsertAt 在某一位置...
这部分可能涉及构造函数、访问修饰符(public、private、protected、默认)、抽象类和接口、以及this关键字和super关键字的使用。 3. **数组与集合框架**:数组是存储固定数量相同类型元素的容器,而集合框架(如...
根据给定文件的信息,我们可以总结出以下几个重要的知识点: ### 1. 编译错误与成员变量访问 - **概念解析**:在编程中,如果尝试访问一个私有成员变量(通常是在不同类的情况下),可能会遇到编译错误。这是因为...
10.3 构造函数和析构函数 .119 10.4 小 结 .122 第十一章 方 法 .124 11.1 方法的声明.124 11.2 方法中的参数.125 11.3 静态和非静态的方法.129 11.4 方法的重载.130 11.5 操作符重载.134 11.6 小 ...
JavaBean是一种特殊的Java类,它遵循一些约定,如使用默认无参构造函数、私有属性、公共getter和setter方法等。 #### 5.9 包 `package` 包是Java中用于组织类的一种机制,可以避免类名冲突,并控制类的可见性。 #...
由于基本无法编辑录制的文件,所以录制的时候为了避免录制失败,推荐纯粹使用键盘操作,以下是比较关键的几个键盘组合: Ctrl+F = 调出查找对话框 Ctrl+H = 调出替换对话框 Alt+F4 = 关闭作用,比如,关闭查找...
由于基本无法编辑录制的文件,所以录制的时候为了避免录制失败,推荐纯粹使用键盘操作,以下是比较关键的几个键盘组合: Ctrl+F = 调出查找对话框 Ctrl+H = 调出替换对话框 Alt+F4 = 关闭作用,比如,关闭查找...
由于基本无法编辑录制的文件,所以录制的时候为了避免录制失败,推荐纯粹使用键盘操作,以下是比较关键的几个键盘组合: Ctrl+F = 调出查找对话框 Ctrl+H = 调出替换对话框 Alt+F4 = 关闭作用,比如,关闭查找...