六、构造函数和析构函数

1.如果没有构造函数?

面向对象的思想是从生活中来,手机、车出厂时,是一样的。生活中存在的对象都是被初始化后才上市的;初始状态是对象普遍存在的一个状态的 。

如果不用构造函数初始化,该怎么办:

  • 为每个类都提供一个public的initialize函数;
  • 对象创建后立即调用initialize函数进行初始化。

缺点

1)initialize只是一个普通的函数,必须显示的调用
2)一旦由于失误的原因,对象没有初始化,那么结果将是不确定的没有初始化的对象,其内部成员变量的值是不定的。

#include 
using namespace std;
class Test
{
  public:
    void init(int a, int b)
      {
          m_a = a;
          m_b = b;
      }
  private:
     int m_a;
     int m_b;
};

int main(void)
{
  Test t1;
   int a = 10;
   int b = 20;
   t1.init(a,b);
   Test tArray[3]; //手动调用显示初始化函数
   tArray[0].init(0,0);
   tArray[1].init(0,0);
   tArray[2].init(0,0);

   Test t21; //手动调用显示初始化函数
    t21.init(0,0);
   
   Test t22;//手动调用显示初始化函数
   t22.init(0,0);

   Test t23;//手动调用显示初始化函数
   t23.init(0,0);//在这种场景之下显示的初 始化方案显得很蹩脚

   Test tArray2[3] ={t21,t22,t23};//在这种场景之下,满足不了,编程需要

   Test tArray3[1999] ={t21,t22,t23};

   return 0;
}

所以C++对类提供了一个给对象的初始化方案,就是构造函数.

2.构造函数

定义:
C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数.

class 类名
{
  类名(形式参数){
      构造体
   }
}

class A
{
    A(形参){
       }
}


调用:

自动调用:一般情况下C++编译器会自动调用构造函数.
手动调用:在一些情况下则需要手工调用构造函数.

2.1构造函数的分类及调用

class Test
{
  public:
       //无参数构造函数
    Test(){
          m_a = 0;
          m_b = 0;
          p = (char*)malloc(100);
          strcpy(p,"abc123");
         cout<<"Test()无参构造函数执行"<

规则:
1 在对象创建时自动调用,完成初始化相关工作。
2 无返回值,与类名同,默认无参,可以重载,可默认参数。
3 一经实现,默认不复存在

3.析构函数

定义
C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数.

class 类名
{
    ~类名(){
      析构体
     }
}

class A
{
   ~A()
     {}
}

规则:
1 对象销毁时,自动调用。完成销毁的善后工作。
2 无返值 ,与类名同。无参。不可以重载与默认参数
3 析构函数的调用顺序,跟对此昂的构造顺序相反,谁先构造,谁最后一个被析构

析构函数的作用,并不是删除对象,而在对象销毁前完成的一些清理工作。

4.默认的拷贝构造和默认构造函数

class A
{
   public:
     /*
     默认的构造函数
     如果普通构造函数,提供了一个显示的构造,那么这个无参的默认构造就会被隐藏,但不会把拷贝构造函数隐藏
     A(){
     }
     */

    /*
    默认的拷贝构造函数
    A(const A &a){
       m_a = a.m_a;
    }
    */
//显示的提供一个拷贝构造函数的时候,默认的拷贝构造函数就会被隐藏

//同样,只有提供一个显示的析构函数,就会将默认的析构覆盖掉
   private:
         int m_a;
};

int main(void)
{
   A aObj;//当你不提供任何构造函数的时候,系统会有一个默认的构造函数
   A aObj2 = aObj;//调用了aObj2的拷贝构造函数

}

二个特殊的构造函数
1)默认无参构造函数
当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空

2)默认拷贝构造函数当类中没有定义拷贝构造函数时,编译器默认提供一个默认拷贝构造函
数,简单的进行成员变量的值复制

5.构造函数规则

  • 系统提供默认的拷贝构造器。一经实现,不复存在。
  • 系统提供的时等位拷贝,也就是所谓的浅浅的拷贝。
  • 要实现深拷贝,必须要自定义。
class Teacher
{
   public:
     Teacher(int id,char *name){
      m_id = id;
   
     //给姓名赋值
      int len = strlen(name);
      m_name = (char*)malloc(len+1);
      strcpy(m_name,name);
     }

     Teacher(const Teacher &another){
         m_id = another.m_id;
         int len = strlen(another.m_name);
         m_name = (char*)malloc(len+1);
         strcpy(m_name,another.m_name);
     }

      ~Teacher(){
       //在构造函数中,已经开辟了内存,为了防止泄露
      //在析构函数中,在对象销毁之前,把m_name的内存释放掉
      if(m_name != NULL){
         free(m_name);
         m_name = NULL;
         cout<<"释放了m_name"<

6.构造函数的初始化列表

如果我们有一个类成员,它本身是一个类或者是一个结构,而且这个成员它只有一个带参数的构造函数,没有默认构造函数。这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数

如果没有初始化列表,那么他将无法完成第一步,就会报错。

class A
{
    public:
        A(int a){
             m_a = a; 
         }
        void printA(){
           cout<<"a = "<

当类成员中含有一个const对象时,或者是一个引用时,他们也必须要通过成员初始化列表进行初始化,因为这两种对象要在声明后马上初始化,而在构造函数中,做的是对他们的赋值,这样是不被允许的。

初始化列表中的初始化顺序,与声明顺序有关,与前后赋值顺序无关。

#include    
using namespace std;
class  ABCD
{
public:
    ABCD(int a, int b, int c)
    {
        _a = a;
        _b = b;
        _c = c;
        printf("ABCD()  construct,  a:%d,b:%d,c:%d\n", _a, _b, _c);
    }
    ~ABCD()
    {
        printf("~ABCD() construct,a:%d,b:%d,c:%d        \n", _a, _b, _c);
    }

    int getA()
    {
        return  _a;
    }

private:
    int _a;
    int _b;
    int _c;
};

class   MyE
{
public:
    MyE() :abcd1(1, 2, 3), abcd2(4, 5, 6), m(100)
    {
        cout << "MyE()" << endl;
    }

    MyE(const   MyE &   obj) :abcd1(7, 8, 9), abcd2(10, 11, 12), m(100)
    {
        printf("MyE(const MyE() &   obj)\n");
    }

    ~MyE()
    {
        cout << "~MyE()" << endl;
    }
public:
    ABCD    abcd1;  //c++编译器不知道如何构造abc1
    ABCD    abcd2;
    const int   m;
};

int doThing(MyE mye1) //mye1 = myE //mye1.拷贝构造函数(myE)
{
    printf("doThing()   mye1.abc1.a:%d \n", mye1.abcd1.getA());

    return 0;
}


int run()
{
    MyE myE; //调用的无参构造函数

    doThing(myE);

    return 0;
}

int run2()
{
    printf("run2    start..\n");

    ABCD(400, 500, 600);    //临时对象的⽣命周期       //会产生一个临时的匿名对象。
    //再次析构匿名对象                                                      //匿名的临时对象,编译器会立刻销毁。不等到正常函数调用完毕。,
    ABCD    abcd    =   ABCD(100,   200,    300);

    printf("run2    end\n");

    //在此析构abcd
    return 0;
}


int main(void)
{
    //run();
    run2();

    return 0;
}

你可能感兴趣的:(六、构造函数和析构函数)