• 【c++】构造函数和析构函数{生可带来,死不带去}


    构造函数:

    对象和内存的关系:

    有空间不一定有对象。
    有对象一定有空间。

    class Empty
    {
    };
    int main()
    {  //看不见构造函数,是因为,只要程序运行,系统会调用默认构造函数,创建对象
       Empty e;   //占位符,有对象,一定要有空间
       cout<<sizeof(e)<<endl;
       Empty *p = &e;  //如果e没空间,指向哪?
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    1.1 构造函数的用途:

    1.创建对象
    2.初始化对象中的属性。
    3.类型转换

    类型转换:

    class Int
    {
    public :
    Int(x = 0):value(x){}
    }
    int main()
    {
    Int ix (12);
    int a = 200;
    ix = a; //赋值给ix的时候,会创建调动构造函数一个无名对象,将a传给ix(完成了类型转换。)
    }

    1. 构造函数的返回值是构造函数创建的对象。(系统分配的空间中)
    2. 在程序运行的时,当新的对象被建立,该对象所属的类的构造函数自动被调用,在该对象的生存期中也只调用一次。
      请添加图片描述
      不可以重新构建c1对象,因为没调动一次构造函数,就会创建一个对象/
      但是new(&c1) Comlex(200,300),可以让c1再构建一次。
    3. 【构造函数可以重载】,严格意义上讲,类中定义了多个构造函数:,(每个构造函数中可以完成不同的属性,生可带来,死了带不走,直接析构了)
    class Complex
    {
         double Real;  
         double Image;
         public:
             Complex(){}
             Complex(double r,double i)//函数重载
             {
                   Real = r;
                   Image = i;
             }
             fun(Complex cx)
             {}
    }
    int main()
    {
         Complex c1; //调用的Complex()默认构造函数
         Complex c2(1.1,2.2);//调用的是Complex(double r,double i)
         Complex c3();//一定要写数据,负责会被认为是函数的声明
         complex c4{};//可以,列表的初始化方案
         fun(Complex(2,3));//构造函数一定要获取某个空间,在某个空间构造对象。
         
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    Complex c3();/一定要写数据,负责会被认为是函数的声明
    4. 构造函数可以在类中定义,也可以在类外定义
    5. 如果类说明中没有给出构造函数,C++编译器会自动生成一个缺省构造函数
    complex(){}
    只要我们自己定义一个构造函数,系统就不会自动生成缺省构造函数,
    如果构造函数不带参数或者带参数没有缺省值(如下图),就会被认为缺省构造函数。
    请添加图片描述

    关键字:sizeof(),编译期确定大小。
    构造函数一定要获取某个空间,在某个空间构造对象。

    1.2 构造函数具有this指针

    class Complex
    {
      private:
         double Real;  
         double Image;
         public:
             Complex(){}//Complex(Complex *const this)
    }
     Complex c1; Complex(&c1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    1.3 结构体和类的区别:

    struct 内默认为public
    class 内默认为private

    struct str
    {
       int a;
       char b;  //可以通过str s; s.a =1,s.b = "s";进行初始化
    }
    class Com
    {
       privateint  real;  //private: int real;int image;
         public :            //public: Com(int r = 0,int i = 0)
               int  image         // Com c3{3,4}   Com c2 = {2}; 均可初始化,从右往左
    }
    int main()
    {
        Com c1 = {1}; //错误,
        Com c2 = {2}; //错误 必须是成员属性均为公有,才可以初始化,
                     //系统会自动提供一个有两个形参的构造函数。
                     // Com(int ,int);
         Com c3{3,4}   ;        
      }                  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 缺省构造函数是否有【形参】取决于定义的成员属性公有还是私有
      公有如下图:有几个属性,缺省构造函数就有几个形参,并且每个形参的缺省给为0
      请添加图片描述
      私有如下图
      请添加图片描述

    1.4 对构造函数使用初始化列表:

    初始化和设计顺序必须相同。

    class Complex
    {
      private:
         double Real;  
         double Image;
         public:
             Complex():real(0),image(0)//成员初始化列表
             {}
              Complex(double r,double i):real(r),image(i)
             {
                   Real = r;
                   Image = i;
             }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    可能出现的问题:

    class Complex
    {
    private:
        int real;
        int image;    
    public:                    
        Complex(int x) :image(x), real(image) {}//read(x),image(real)可以
        void print()
        {
            cout << "real:" << real << "image:" << image << endl;
        }
    };
    int main()
    {
        Complex c1(10);
        c1.print();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    列表初始化的顺序最好和属性定义的顺序相同,否则如图。
    请添加图片描述
    类中定义了多个构造函数,:向现实靠拢,每个人的出身不同,所带的属性也就不同

    1.5 构造函数可以用引用的方式返回。

    int &Real()    ,相当于返回this指针指向对象的别名
    {
        return real;
     }
    
    • 1
    • 2
    • 3
    • 4

    析构函数

    【对象的生存期满了就会自动调用。】


    用构造函数创建对象后,程序负责跟踪对象,当对象使用结束,系统就会调用析构函数,完成对对象的清理。
    注意:
    1.析构函数不可重载,无可见参数
    2.析构函数如果自己没有实现,系统会默认生成一个。
    语法:~函数名()

    1. 局部对象,在函数调用点处创建对象,函数结束,对象被析构,先创建的后析构
    2. 静态局部对象(在数据区):等这个程序结束的时候才会被析构。
    3. 全局对象(全文见可见):当程序进入main之前,对象已经定义,整个程序结束,对象才会析构调。(静态全局对象,本文件可见。)
     Complex c1(1,1);
     int main()
    {
        cout<<"begin main"<<endl;
        Complex c1(10);
        cout<<"end main:"<<endl;
        return 0;
    }
    Complex c3(3,3);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    不论全局函数在哪,在进入主函数之前都会定义好,请添加图片描述


    4.块内对象:进入块中调用构造函数创建对象,从块中出来,析构对象。
    //只在块中有效

     int main()
    {
        cout<<"begin main"<<endl;
        Complex c1(11);
        cout<<"begin 块:"<<endl;
        {
            Complex c2(2,2);
        } //这个位置块内对象析构
        cout<<"end 块"<<endl;
        cout<<"end main"<<endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    请添加图片描述

    • 不能控制生,但可以控制死。(可以主动调用析构函数,析构对象)
      一般不要这样,系统无法记录析构函数调动了几次,因此自己析构了,但是系统还会再析构。
     int main()
    {
        Complex cs(11);
       //
       cs.~Complex(); //主动调动。
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5.动态创建的对象, 使用new创建对象,delete释放对象

     int main() 
    {
        int n = 5;
     //   Complex* xp = new Complex[n]{{1,2},{},{4,5},{6,7}};//1.申请堆空间2.创建对象(调用的是有参的构造函数)                         
                                           //  {}调用的是无参构造函数
         Complex* xp = new Complex{}; 
       // xp -> print();   
        for(int i = 0;i<n;i++)
        {
           xp[i].Print();  //申请了一组对象
        }
      //  delete xp; //1.析构对象,将内存还给申请空间,2.将申请的空间还给堆
        delete []xp;
        xp = nullptr;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    请添加图片描述
    有空间不一定有对象,必须要调用构造函数创建对象。
    所以,不能调用对象的方法。
    有对象才可以对空间进行操作。
    如下:

    int main()
    {
        Complex *xp = (Complex*)malloc(sizeof(Complex));//只申请空间,没有兑现
        //调动构造函数再new指定的xp申请的空间中构建对象
        new (xp) Complex{2,3};//定位new,构建对象。这样
        xp->print();                   // xp才可以访问对象的方法。
       xp->~Complex();//主动析构对象。
        free(xp);
        xp = NULL
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    所以malloc + 定位new实现了 new 的功能。


    成员方法的设计

    1. 常对象只能调动常方法
    2. 普通对象可以调动普通方法,也可以调动常方法
    3. 普通对象优先调用普通方法
      原因:对象调用类中的方法,本质上是将对象的地址传给this指针,因此对象被const修饰,在被this指针指向的时候,能力只能缩小,不能扩大。

    当类中的成员属性设置为私有的时候,可以通过成员方法访问或修改成员属性。
    如:

    class Complex
    {
        private:
              int  num;
              int  age;
        public:
              int set_num(int n)
              {
                   num  = n;
              }
              // int set_age(const Complex *const this ,int a) 
              int set_age(int a) const //常方法  但是导致无法对age进行赋值
              {
                  age = a;
              }
               int get_num() const   //在获取数据的时候加const,只读不写
              {
                  return num;
              }
                 int get_age() const
              {
                  return age;
              }
              
              
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    int main()
    {
        Complex cc;
        cc.set_num(10);  //通过成员方法。。。
        const Complex ss;  //常对象  ,约束的是指向能力,一般用于获取数据
         ss.set_age(20);  //只能访问常方法
         cc.set_age(10);//也可以。能力缩小 导致无法写入数据
         //在获取数据的时候加const,只读不写
         ss.get_age();
         cc.get_num();
         }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    常方法里只能调动其他常方法。

    主要就是this指针的可访问性权限,指向的能力
    如下:

    class Complex
    {
        private:
              int  num;
              int  age;
        public:
              int set_num(int n)
              {
                   num  = n;
              }
              //int print(const Complex *const this,int n)
              int print(int n)const
              {
                 //
                 this->set_num(n)//错误,因为set_num(n) 相当于set_num(this,n),print的this 放入sert_num,能力被扩大
              }
              // int set_age(const Complex *const this ,int a) 
              
               int get_num() const   //在获取数据的时候加const,只读不写
              {
                   this->print();//  print(this)  常方法可以调用常方法
                  return num;
              }
                 int get_age() 
              {  
                 this->print();  //普通方法也可以调用常方法,因为可以this指针传给print的this,能力被缩小。
                  return age;
              }
              
              
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
  • 相关阅读:
    软件改为开机自启动
    CLion 总是提示 “This file does not belong to any project target xxx” 的解决方法
    istio: 如何对istio数据平面进行benchmark
    Redis服务部署
    理解傅里叶变换
    arima模型python代码
    【文件读取/包含】任意文件读取漏洞 afr_2
    域名系统安全作业-DNS Cache Poisoning Attack Reloaded: Revolutions with Side Channels
    基于图的路径搜索技术基础知识
    C++ //练习 10.2 重做上一题,但读取string序列存入list中。
  • 原文地址:https://blog.csdn.net/weixin_52958292/article/details/127427493