C++(1)C++类四个默认函数---构造函数、析构函数、拷贝函数、赋值函数
C++构造函数和析构函数
默认构造函数指不带参数或者所有参数都有缺省值的构造函数!!!
(1)构造函数、析构函数与赋值函数
构造函数、析构函数与赋值函数是每个类最基本的函数。它们太普通以致让人容易麻痹大意,
其实这些貌似简单的函数就象没有顶盖的下水道那样危险。
每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数)。对于任意一个类A,如果不想编写上述函数,C++编译器将自动为A 产生四个缺省的函数,例如:
A(void); // 缺省的无参数构造函数 A(const A &a); // 缺省的拷贝构造函数 ~A(void); // 缺省的析构函数 A & operate =(const A &a); // 缺省的赋值函数
这不禁让人疑惑,既然能自动生成函数,为什么还要程序员编写?原因如下:
<1>如果使用“缺省的无参数构造函数”和“缺省的析构函数”,等于放弃了自主“初始化”和“清除”的机会,C++发明人Stroustrup 的好心好意白费了。
<2>“缺省的拷贝构造函数”和“缺省的赋值函数”均采用“位拷贝”而非“值拷贝”的方式来实现,倘若类中含有指针变量,这两个函数注定将出错。
对于那些没有吃够苦头的C++程序员,如果他说编写构造函数、析构函数与赋值函数很容易,可以不用动脑筋,表明他的认识还比较肤浅,水平有待于提高。
下面以类String 的设计与实现为例,深入阐述被很多教科书忽视了的道理。String的结构如下:
class String { public: String(const char *str = NULL); // 普通构造函数 String(const String &other); // 拷贝构造函数 ~ String(void); // 析构函数 String & operate =(const String &other); // 赋值函数 private: char *m_data; // 用于保存字符串 };
(2)构造函数是一种特殊的成员函数,无返回值,函数名与类同名。它提供了对成员变量进行初始化的方法,使得在声明对象时能自动地初始化对象。因为当程序创建一个对象时,系统会自动调用该对象所属类的构造函数。
例一:class Student { Student()//默认无参无赋值操作构造函数 { } } Student stu;//声明对象
以上代码中的无参无操作构造函数即为系统自动提供一个默认的构造函数,该默认构造函数没有参数,它仅仅负责创建对象而不做任何赋值操作。
例二:class Student { Student()//无参带赋值操作构造函数 { memberVariable1=constValue1; memberVariable2=constValue2; } } Student stu;//声明对象
以上代码中,在默认构造函数添加赋值初始化操作,该构造函数将覆盖默认构造函数。该构造函数没有参数,它不仅负责创建对象而还负责成员变量的状态初始化。
例三:class Student { Student(type1 value1,type2 value2) //含参带赋值操作构造函数 { memberVariable1=value1; memberVariable2=value2; } } Student stu(value1,value2);//声明对象
以上代码中,在默认构造函数中添加参数和赋值初始化操作,该构造函数将覆盖默认构造函数。该构造函数没有参数,它不仅负责创建对象而还负责传值对成员变量进行状态初始化。
一旦类中有了一个带参数的构造函数而又没无参数构造函数的时候系统将无法创建不带参数的对象,此时以下三种声明都是错误的:
Student stu;
Student *stu = new Student;
Student *stu = new Student();
例四:class Student { Student() { } /*Student() { memberVariable1=constValue1; memberVariable2=constValue2; }*/ Student(type1 value1,type2 value2) { memberVariable1=value1; memberVariable2=value2; } }; Student stu; // 声明对象—栈对象 Student *stu; // 类指针变量—栈对象 Student *stu = new Student; // çèStudent *stu = new Student();—堆对象 Student stu(value1,value2); // 声明对象—栈对象 Student *stu = new Student(value1,value2); // 声明对象—堆对象
以上代码中,既有无参(默认)构造函数,又有含参和赋值操作的构造函数;既可声明无参对象,也可声明含参初始化对象。注意new是在堆上动态创建的。
由于构造函数和普通函数一样具有重载特性所以编写程序的人可以给一个类添加任意多个构造函数,来使用不同的参数来进行初始化对象!
类一旦定义就可以当作一种新的数据类型,可作为另一个类的数据成员,即类可以嵌套定义。
类是一个抽象的概念,并不是一个实体,并不能包含属性值(这里来说也就是构造函数的参数了),只有对象才占有一定的内存空间,含有明确的属性值!
一个类可能需要在构造函数内动态分配资源,那么这些动态开辟的资源就需要在对象不复存在之前被销毁掉,那么c++类的析构函数就提供了这个方便。
(3)构造函数的初始化表
构造函数有个特殊的初始化方式叫“初始化表达式表”(简称初始化表)。初始化表位于函数参数表之后,却在函数体 {} 之前。这说明该表里的初始化工作发生在函数体内的任何代码被执行之前。
构造函数初始化表的使用规则:
<1> 如果类存在继承关系,派生类必须在其初始化表里调用基类的构造函数。例如:
class A { … A(int x); // A 的构造函数 }; class B : public A { … B(int x, int y);// B 的构造函数 }; B::B(int x, int y): A(x) // 在初始化表里调用A 的构造函数 { … }
<2>类的 const 常量只能在初始化表里被初始化,因为它不能在函数体内用赋值的方式来初始化。
注:不要在类定义.h文件里声明const常量时直接初始化,会有警告,只有在c++11中才有效
如:
//A.h class A
{
const int n =;
}
报警告:
这个与C语言中const定义常对象时,同样要进行初始化,并且该对象不能再被更新写法上有所区别,但是其实意义是一样的,只不过c++中再初始表中初始,同样不可更新。
<3>类的数据成员的初始化可以采用初始化表或函数体内赋值两种方式,这两种方式的效率不完全相同。
[1]非内部数据类型的成员对象应当采用第一种方式初始化,以获取更高的效率。例如:
class A
{ …
A(void); // 无参数构造函数 A(const A &other); // 拷贝构造函数 A & operate =( const A &other); // 赋值函数
}; class B
{
public:
B(const A &a); // B 的构造函数 private:
A m_a; // 成员对象 };
示例 9-2(a)中,类B 的构造函数在其初始化表里调用了类A的拷贝构造函数,从而将成员对象m_a 初始化。
示例 9-2 (b)中,类B 的构造函数在函数体内用赋值的方式将成员对象m_a 初始化。我们看到的只是一条赋值语句,但实际上B 的构造函数干了两件事:先暗地里创建m_a对象(调用了A 的无参数构造函数),再调用类A 的赋值函数,将参数a 赋给m_a。
示例 -(a) 成员对象在初始化表中被初始化: B::B(const A &a) : m_a(a)
{
…
} 示例9-(b) 成员对象在函数体内被初始化: B::B(const A &a)
{
m_a = a;
…
}
[2]对于内部数据类型的数据成员而言,两种初始化方式的效率几乎没有区别,但后者的程序版式似乎更清晰些。若类F的声明如下:
class F
{
public:
F(int x, int y); // 构造函数 private:
int m_x, m_y;
int m_i, m_j; }
示例9-2(c)中F 的构造函数采用了第一种初始化方式,示例9-2(d)中F 的构造函数采用了第二种初始化方式。
示例 -(c) 数据成员在初始化表中被初始化: F::F(int x, int y) : m_x(x), m_y(y)
{
m_i = ;
m_j = ;
} 示例9-(d) 数据成员在函数体内被初始化: F::F(int x, int y)
{
m_x = x;
m_y = y;
m_i = ;
m_j = ;
}
(4)拷贝构造函数和赋值函数的区别
拷贝构造函数和赋值函数非常容易混淆,常导致错写、错用。拷贝构造函数是在对象被创建时调用的,而赋值函数只能被已经存在了的对象调用。以下程序中,第三个语句和第四个语句很相似,你分得清楚哪个调用了拷贝构造函数,哪个调用了赋值函数吗?
String a(“hello”);
String b(“world”);
String c = a; // 调用了拷贝构造函数,最好写成c(a);
c = b; // 调用了赋值函数
(5)析构函数也是特殊的类成员函数,它没有返回类型,没有参数,不能随意调用,也没有重载,只有在类对象的生命期结束的时候,由系统自动调用,用来在系统释放对象前做一些清理工作,如利用delete运算符释放临时分配的内存、清零某些内存单元等。
定义析构函数因使用"~"符号加类名(逻辑非运算符),表示它为逆构造函数,它不能带任何参数。
转自:http://allchange.blog.sohu.com/155795465.html
注:内部数据类型 非内部数据类型
内部数据类型是编译器本来就认识的,不需要用户自己定义,如int 、char
非内部数据类型不是编译器本来就认识的,需要用户自己定义才能让编译器识别
运算符使用是否正确,编译器在编译扫描分析时就可以判定
库函数是已编译的代码,编译器不会编译检查,由链接器将库同用户写的代码合成exe文件
C++(1)C++类四个默认函数---构造函数、析构函数、拷贝函数、赋值函数的更多相关文章
- c++类大四个默认函数-构造函数 析构函数 拷贝构造函数 赋值构造函数
每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数).对于任意一个类A,如果不编写上述函数,C++编译器将自动为A 产生四个缺省的函数,例如: A ...
- C++类四个默认函数&深复制&浅复制
学习C++语言的同学都知道,C++中类是有默认的几个函数的,主要是有四个函数: 四个函数 默认构造函数:A(void),无参构造函数 拷贝(复制)构造函数:A(const A&a).用一个对象 ...
- C++类的const成员函数、默认的构造函数、复制形参调用函数(转)
C++类的const成员函数 double Sales_item::avg_price() const { } const关键字表明这是一个const成员函数,它不可以修改Sales_item类的成员 ...
- C++ 类 & 对象-类成员函数-类访问修饰符-C++ 友元函数-构造函数 & 析构函数-C++ 拷贝构造函数
C++ 类成员函数 成员函数可以定义在类定义内部,或者单独使用范围解析运算符 :: 来定义. 需要强调一点,在 :: 运算符之前必须使用类名.调用成员函数是在对象上使用点运算符(.),这样它就能操作与 ...
- 2016/3/21 面向对象: ①定义类 ②实例化对象 ③$this关键字 ④构造函数 ⑤析构函数 ⑥封装 ⑦继承
一:定义类 二:实例化对象 //定义类 class Ren { var $name; var $sex; var $age; function Say() { echo "{$this- ...
- CPP_类默认函数:构造函数,拷贝构造函数,赋值函数和析构函数
类默认函数:构造函数,拷贝构造函数,赋值函数和析构函数 // person.h #ifndef _PERSON_H_ #define _PERSON_H_ class Person{ public : ...
- 堆(stack) 之 c 和 c++模板实现(空类默认成员函数 初谈引用 内联函数)
//stack 的基本操作 #include <iostream> using namespace std; const int maxn = 3; typedef struct Stac ...
- C++中:默认构造函数、析构函数、拷贝构造函数和赋值函数——转
对于一个空类,编译器默认产生4个成员函数:默认构造函数.析构函数.拷贝构造函数和赋值函数.1.构造函数:构造函数是一种特殊的类成员,是当创建一个类的时候,它被调用来对类的数据成员进行初始化和分配内存. ...
- C++基础知识 基类指针、虚函数、多态性、纯虚函数、虚析构
一.基类指针.派生类指针 父类指针可以new一个子类对象 二.虚函数 有没有一个解决方法,使我们只定义一个对象指针,就可以调用父类,以及各个子类的同名函数? 有解决方案,这个对象指针必须是一个父类类型 ...
随机推荐
- c#的SortedList使用方法
表示键/值对的集合,这些键和值按键排序并可按照键和索引访问. SortedList最合适对一列健/值对 进行排序,在排序时,是对键进行排序,SortedList 是 Hashtable 和 Array ...
- asp.net导出excel-一行代码实现excel、xml、pdf、word、html、csv等7种格式文件导出功能而且美观-SNF快速开发平台
分享: 腾讯微博 新浪微博 搜狐微博 网易微博 腾讯朋友 百度贴吧 豆瓣 QQ好友 人人网 作者:王春天 原文地址:http://www.cnblogs.com/spring_ ...
- 从jar包中读取资源
package myspider; import java.io.UnsupportedEncodingException; /** * * @author mark */ public class ...
- jenkins 发送邮件模板
jenkins 发送邮件模板 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> ...
- unity 在脚本B中调用脚本A的函数
一,在脚本B中调用脚本A的函数. 脚本A: //myFuncs.cs using UnityEngine;using System.Collections; namespace myFuncs{ ...
- hadoop详细了解5个进程的作用
1.job的本质是什么?2.任务的本质是什么?3.文件系统的Namespace由谁来管理,Namespace的作用是什么?4.Namespace 镜像文件(Namespace image)和操作日志文 ...
- 关于WCF服务的调试跟踪
关于WCF服务的调试跟踪信息,请利用好以下几个工具,具体的例子MSDN上都有,进去看吧... 服务跟踪查看器工具 (SvcTraceViewer.exe): http://msdn.microsoft ...
- php教程之php空白页的原因及解决方法
php中出现空白页的原因及解决方法汇总. 很多程序员在进行php开发的时候都遇到过出现空白页的请,综合分析来说,在php编程中出现空白页面可能是由以下几个原因造成的: 1.逻辑错误逻辑错误是最难排除的 ...
- .NET执行SQL插入时间的问题
错误描述: 一个项目,源码是BOSS给的,部署到网上了,运行没有问题,可是在本地运行,就会有问题,问题在于往一些表插入记录的时候,本地不管怎么样都插入不了,而网上就可以插入,都是相同的一份代码 解决: ...
- java playframework
刚学了java不久就让我们尝试架构,就选择了一个比較简单的架构 play framework直接上图 这里的执行环境是JDK1.7,我试了一下jdk1.8不行出问题了play使用的是play-1.2. ...