zoukankan      html  css  js  c++  java
  • C++构造函数与析构函数

    转自http://blog.csdn.net/tqtuuuu/article/details/6652144

    构造函数

        对于C++的构造函数,暂且将其分为以下几类:

    1. 默认构造函数

    2. 隐士转换构造函数

    3. 拷贝构造函数

    4. 其它构造函数

        1. 默认构造函数表示没有任何参数的构造函数,当自定义任何构造函数以后,将不再自动创建默认构造函数,当然,默认构造函数啥也不干,程序员关心系数顿时大跌。关于默认构造函数还需要关心的一个问题是,当本类继承于另一个类(即父类),父类没有默认构造函数时,本类将自动没有构造函数,并且还必须定义至少能够支持父类初始化的构造函数。

        2. 隐士转换构造函数时什么呢?它是这样一类构造函数,这类构造函数仅含有一个参数。由于C++规定,为只含有一个参数的构造函数自动定义一种隐士转换,将构造函数的参数对应数据类型的对象转换为类对象。如下面例子所示:

    1 class String {
    2      String(const char *s);
    3 };
    4 
    5 String s = "hello friends";

      其中字符串“hello friends”通过隐士转换构造函数转换为了String类对象s。这里不得不提一个关键字explicit,比较悲剧的是学了好几年C++后才知道这么一个关键字,这也充分体现了一本好书的重要性。下面来看看explicit的用法,再解释。

    1 class String {
    2     explicit String(const char *s);
    3 };
    4 
    5 String s = "hello friends"; //编译不通过
    6 
    7 String s("hello friends");  //编译通过

    explicit仅用于构造函数,且仅用于隐士转换构造函数,其目的是禁止隐士构造函数功能。如上代码所示,当为构造函数添加explicit关键字后,String s = "hello friends"; 编译不通过。当然,此时构造函数本身还是能够正常工作的。

        3. 拷贝构造函数,就是用来复制对象的一种特殊的构造函数。通过它,可以使用一个已经创建好的对象(由拷贝构造函数的参数指定)去初始化一个正准备创建的同类对象。

    1 class 类名
    2 {
    3 public:
    4     类名(类名 &对象名);
    5 };

    如上述代码所述,拷贝构造函数只能有一个参数,并且必须是同类对象的引用。每个类都应该有一个拷贝构造函数,如果用户没有指定,将使用默认的拷贝构造函数。默认拷贝构造函数执行对象的全部内容复制,但有时需要有选择、有变化地复制。这种情况就需要显示定义拷贝构造函数,并实现为感兴趣部分的复制或者为新对象添加某些属性值。

        拷贝构造函数最常见的使用方式是在类中含有指针类型属性时,因为默认的拷贝函数是按指拷贝的,即所谓的浅拷贝。这时需要显示定义拷贝构造函数,并显示地将指针从新分配内存并copy以前指向的值到新分配的内存中。

        4. 其它构造函数,没什么特别的,就是前面1,2,3未包含的构造函数均属于该类。

       

     构造函数初始化参数列表

        以下情况下需要使用初始化成员列表:(待补充)

    一、需要初始化的数据成员是对象的情况;

    二、需要初始化const修饰的类成员;

        第一种情况,如果类A中属性存在一个类B,且类B没有不带参数的构造函数,此时初始化A时也需要初始化B,所以需要初始化参数列表。当然,此种情况一般成员属性也可以通过此种方式初始化。

        第二种情况,const成员属性不能复制,一旦初始化后就不能改变。

        下面的例子说明构造函数初始化参数列表的使用:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 class B
     5 {
     6 public:
     7     B(int b) {
     8         cout<<b<<endl;
     9     }
    10 };
    11 
    12 class A
    13 {
    14 public:
    15     B b;
    16     A(int a, int ci);
    17 private:
    18     const int ci;
    19     //const int ci = 10;    //编译错误,不能初始化  
    20     static const int cv = 10; //添加static关键字后能够编译通过
    21     int c;
    22 };
    23 
    24 A::A(int a, int ci):b(a),ci(ci),c(4) {
    25     cout << ci << "--" << c << endl;
    26 }
    27 
    28 int main()
    29 {
    30     A a(5, 10);
    31     return 0;
    32 }

    析构函数

       析构函数就与构造函数对应,在对象消亡时做一些善后处理。看下面的例子:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 class A
     5 {
     6 public:
     7     A() {
     8         cout << "A..." << endl;
     9     }
    10     ~A() {
    11         cout << "~A" <<endl;
    12     }
    13 };
    14 
    15 class B : public A
    16 {
    17 public:
    18     B() {
    19         cout<<"B..."<<endl;
    20     }
    21     ~B() {
    22         cout<<"~B"<<endl;
    23     }
    24 };
    25 
    26 int main() {
    27     A *a = new B();
    28     delete a;
    29 
    30     return 0;
    31 }<span style="font-family: Arial, Verdana, sans-serif; white-space: normal; background-color: rgb(255, 255, 255); ">&nbsp;</span>

    编译输出:

    1 # ./a.out 
    2 A...
    3 B...
    4 ~A

     由此可见,在对象a消亡时,会自动调用对象A的析构函数。但是C++也太傻了吧,明明我实例化的B对象,可为啥析构时是调用A对象的析构函数呢?看delete a,由此可估计delete关键字只认a的对象,而不会变通。怎么能够这样...

    virtual关键字

         上面的例子显示,构造了对象B,却没有析构,这会造成啥问题呢?反正我是不清楚了。于是C++提供的virtual关键字可以解决这个问题,看下面的代码:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 class A
     5 {
     6 public:
     7     A() {
     8         cout << "A..." << endl;
     9     }
    10     virtual ~A() {            //唯一区别:添加了关键字virtual
    11         cout << "~A" <<endl;
    12     }
    13 };
    14 
    15 class B : public A
    16 {
    17 public:
    18     B() {
    19         cout<<"B..."<<endl;
    20     }
    21     ~B() {
    22         cout<<"~B"<<endl;
    23     }
    24 };
    25 
    26 int main() {
    27     A *a = new B();
    28     delete a;
    29 
    30     return 0;
    31 }

      输出:

    1 # ./a.out 
    2 A...
    3 B...
    4 ~B
    5 ~A

      可见,添加virtual关键字后,确实能够解决该问题,B对象也调用了析构函数。可是这是为什么呢?

        下面开始yy了,也许delete并不是那么傻,她会去虚函数表里面查看当前析构函数是否被定义为虚函数,如果是虚函数,它就会想了,那对象会不会实例化的是子类的对象呢,于是再判断是否实例化为子类对象,如果是就先调用子类的析构函数。

  • 相关阅读:
    C语言位操作
    Ribbon负载规则的替换
    Nginx 的配置文件
    Nginx 操作常用的命令
    Nginx 是什么?
    SpringCloud Eureka 新版本依赖
    @Autowired 与@Resource的区别
    spring 注释
    redis 的 rdb 和 aof 持久化的区别
    jdk1.7下HashMap的头插法问题
  • 原文地址:https://www.cnblogs.com/zl1991/p/4679507.html
Copyright © 2011-2022 走看看