(1.1.9) 构造函数,析构函数,拷贝构造函数和赋值函数

     A(void);                    // 缺省的无参数构造函数 

    A(const A &a);              // 缺省的拷贝构造函数 
    ~A(void);                   // 缺省的析构函数 
    A & operate =(const A &a);  // 缺省的赋值函数 


一. 构造函数       A(void);  
1. 构造函数是一种用于创建对象的特殊成员函数,当创建对象时,系统自动调用构造函数。
2. 构造函数名与类名相同,一个类可以拥有多个构造函数(重载:同名不同参),构造函数可以有任意类型的参数,但不能具有返回类型,连Void也不可以,它有隐含的返回值,该值由系统内部使用。
3. 构造函数的作用是:为对象分配空间;对数据成员赋初值;请求其他资源。
4. 每个类必须有一个构造函数,否则没法创建对象;如果一个类没有定义构造函数,编译器会自动生成一个无参的默认构造函数;如果为类定义了一个带参的构造函数,还想要无参构造函数,就必须自己定义;

 

二. 析构函数:    ~A(void);      
1. 析构函数名字为符号“~”加类名,析构函数没有参数和返回值。
2. 一个类中只可能定义一个析构函数,所以析构函数不能重载。 另外,不要在析构函数中抛出异常,不然内存不被回收,容易造成泄露。
3. 析构函数的作用是进行清除对象,释放内存等。当对象超出其定义范围时(即释放该对象时),编译器自动调用析构函数。
   在以下情况下,析构函数也会被自动调用:
   (1)如果一个对象被定义在一个函数体内,则当这个函数结束时,该对象的析构函数被自动调用。
   (2)若一个对象是使用new运算符动态创建的,在使用delete运算符释放它时,delete将会自动调用析构函数。用new创建的对象,必须用delete销毁。
4. delete释放new产生的空间,而delete [] 释放new []产生的数组空间。
   例如:
   class   A   
  {   
      A(){   m_pstr   =   new   char[10];}   
      ~A(){   delete[]   m_pstr;m_pstr   =   NULL;}   
  }   
    
  A   *pObj   =   new   A[10]; 

 

三. 拷贝构造函数:    A(const A &a);   

对于普通类型的对象来说,它们之间的复制是很简单的,例如: 
    int a=100;  int b=a;
而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。 

1. 当构造函数的参数为自身类的引用时,这个构造函数称为拷贝构造函数。拷贝构造函数的功能是用一个已有对象初始化一个正在建立的同类对象。
2. 拷贝构造函数的特点如下:
(1)该函数名与类同名,因为它也是一种构造函数,并且该函数也不被指定返回类型;
(2)该函数只有一个参数,并且是对某个对象的引用;
(3)每个类都必须有一个拷贝构造函数;
(4)如果程序员没有显式地定义一个拷贝构造函数,那么,C++编译器会自动生成一个缺省的拷贝构造函数.
(5)拷贝构造函数的目的是建立一个新的对象实体,所以,一定要保有证新创建的对象有着独立的内存空间,而不是与先前的对象共用。
     在定义一些类时,有时需要(而且强立推荐)显式地定义拷贝构造函数。
3.拷贝构造函数主要在如下三种情况中起初始化作用。
(1)声明语句中用一个对象初始化另一个对象(一定是在"声明并初始化对象"时被调用);例如TPoint P2(P1)表示由对象P1初始化P2时,需要调用拷贝构造函数。
(2)将一个对象作为参数按值调用方式(而不是指针)传递给另一个对象时生成对象副本(即 复制构造函数在"对象作为函数参数"时被调用)。当对象作为函数实参传递给函数形参时,如p=f(N),在调用f()函数时,对象N是实参,要用它来初始化被调用函数的形参,这时需要调用拷贝构造函数。
(3)生成一个临时的对象作为函数的返回结果。但对象作为函数返回值时,如return R时,系统将用对象R来初始化一个匿名对象,这时需要调用拷贝构造函数。
4.拷贝构造函数的执行:
(1)用已有对象初始化创建对象。 
(2)当对象作函数参数时,因为要用实参初始化形参,也要调用拷贝构造函数。
(3)函数返回值为类类型时,情况也类似。

 

四. 赋值函数:    A & operate =(const A &a); 
1. 赋值操作符则给对象一个新的值,既然是新的值,说明这个对象原来就有值,这也说明,赋值函数只能被已经存在了的对象调用。
2. 如果不主动编写拷贝构造函数和赋值函数,编译器将以“位拷贝”的方式自动生成缺省的函数。倘若类中含有指针变量,那么这两个缺省的函数就隐含了错误。

 

五. 成员初始化的必要性(摘自<<C++程序设计语言(特别版)>>第221页,略作修改).

1.  对于那些初始化与赋值不同的情况,主要有以下两种情况:1) 没有默认构造函数的类的成员对象; 2)const 成员和引用成员,对成员的初始式(即初始化列表)是必不可少的。例如:

class A {

    string name;

    Date founded;

    A(const string& n, Date fd);

};

 

class X {

    const int i;  // const 成员

    A a;            // 没有默认构造函数的类的成员对象

    A& pa;        // 引用成员

    X(int ii, const string& n, Date d, A& a): i(ii), a(n, d), pa(a) { };

 

不存在对这些成员做初始化的其它方式,而且,不对这种成员初始化就是错误。

除了以上的两种情况必须采用初始式之外,采用初始式(初始化列表)还有以下两大优点:

(1) 这种形式使得初始化过程更加明显;

(2) 会带来效率上的优势。

 

六. 举例: 构造函数,析构函数,拷贝构造函数和赋值函数 的测试程序

 

/*  编辑编译环境:Dev-C++ 4.9.9.2  */

#include "stdio.h"
#include "string.h"


#define NAME_MAXLENGTH 20

 

class Person
{
    public:
           //int Person(); // error: return type specification for constructor invalid 
           //void Person();// error: return type specification for constructor invalid 
           Person()
           {
               printf("Person::Person() is called !!!  /n");
               memcpy(name," NO Name",NAME_MAXLENGTH);
               age = 0;
               sex = 'W';
           }
           Person(char* n, int a, char s)
           {
               printf("Person::Person(char* n, int a, char s) is called !!!  /n");
               memcpy(name,n,NAME_MAXLENGTH);
               age = a;
               sex = s; 
           }
           Person(char *n, int a)  //与前一个构造函数对比,可见,构造函数可以重载 
           {
               printf("Person::Person(char *n, int a) is called !!!  /n");
               memcpy(name,n,NAME_MAXLENGTH);
               age = a;
               sex = 'W'; 
           }
           void disp()
           {
                printf("name: %20s,    ", name);
                printf("age:  %4d,    ", age);
                printf("sex:  %2c    ", sex);
                printf("/n");
           }
           char* getName()
           {
               return name;
           }
           ~Person()
           {
               printf("destory:  %s/n", this->getName());  
           }
           //~Person(int a){ };//error: destructors may not have parameters 
                       //error: `Person::~Person()' and `Person::~Person()' cannot be overloaded

    private:
       char name[NAME_MAXLENGTH];
       int age;
       char sex; // 'M': men;  'W': women
};

void testFunction()
{
    /* 1. 构造函数的测试 */ 
     //Person zh;                  //error: no matching function for call to `Person::Person()'
    Person Wang("Wang",20,'M');    //使用对象, 调用构造函数 
    //Person &xiaoWang;            //error:`xiaoWang' declared as reference but not initialized 
                                   //对于引用类型,一定要在定义的时候进行初始化. 
    Person &LaoWang = Wang;                 //正确使用引用,既不调用copy构造函数,也不调用赋值函数  
    Person *Li = new Person("Li",19);   //使用指针,通过new操作符调用了构造函数 
    
    Person *pStudent = new Person[3];  //调用三次构造函数Person(); 
    
    Wang.disp();
    Li->disp();
    for (int i =0;i++;i<3)
    {
        pStudent[i].disp();
    } 
    
    /* 2. 拷贝构造函数的测试 */
    Person WangWu = Wang; //调用缺省的拷贝构造函数 
    WangWu.disp();
    
    /* 3. 赋值函数的测试 */
    WangWu = *Li;  //调用缺省的赋值函数
    WangWu.disp(); //输出结果和Li的输出结果相同。

    /* 4. 析构函数的测试 */
    delete Li;                 //用new构造的对象必须使用delete进行撤消. 
    delete [] pStudent;        //用new构造的对象必须使用delete进行撤消.
    
    
}

int main()
{
    testFunction();
    while(1);
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结和选择结 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值