• 【C++】类与对象基本知识 (构造 析构 拷贝 explicit 对象数组 动态静态对象)


    目录

    1.类与对象基本概念

    2.构造函数

    3.析构函数

    4.构造和析构函数调用顺序

    5.拷贝构造函数

    6.浅拷贝和深拷贝

    7.初始化列表

    8.explicit防止构造函数隐式转换

    9.对象数组

    10.动态对象

    10.1 动态对象创建

    10.2 动态对象数组

    11.静态成员

    11.1 静态成员变量

    11.2 静态成员函数


    this指针,友元,重载运算符等后续单独出。

    1.类与对象基本概念

    • 类的封装性:将具有共性的数据和方法封装在一起,加以权限区分,限制用户的访问。

    • 类的权限:

      private(私有),protected(保护),public(共有)

      类的内部(class内)没有权限限制,权限限制在类的外部实现。

      默认为私有的。
      private成员:只能被本类成员函数或友元访问。
      public成员:在任何地方都可以访问。
      protected成员:在private的基础上,还可以被派生类访问。

      加了作用域后也相当于在类内。

    2.构造函数

    • 定义:

      • 构造器 constructor

        对类创建的对象进行初始化 特点: 1.名称与类名一致;

        2.没有返回类型;

        3.经常被重载;

        4.每次创建对象都会自动调用构造函数。

      • 默认构造器:default constructor 可以进行无参调用的构造函数

        特点: 1.一个类没有定义构造函数时,c++编译器会自动生成默认构造函数; 2.类中只要有定义了构造函数,c++编译器就不会生成默认构造函数; 3.所有参数都是默认参数的普通构造函数,也可以充当默认构造函数。

      • 调用构造函数各种方式:

      class Data
      {
      public:
      int mA;
      public:
      //无参构造函数
      Data()
      {
      mA=0;
      cout<<"无参构造函数"<3.析构函数 
      
      • 定义

        析构器 destructor

        当对象不再使用时(出了所在作用域),对该对象进行清理工作 特点:(与构造器相反) 1.名称为“~类名”

        2.没有返回类型;

        3.没有参数;

        4.每次析构对象都会自动调用析构函数。

        5.析构函数理论上可写可不写,但有指针的时候必须写,不然指针开辟的空间无法删除。

      4.构造和析构函数调用顺序

      #include
      using namespace std;
      ​
      //创建一个类
      class Data
      {
      private:
           int a;
      public:
           Data();
           Data(int b);
           ~Data();
      };
      //默认构造函数
      Data::Data()
      {
           a = 999;
           cout << "调用默认构造函数:" << a << endl;
      }
      //构造函数
      Data::Data(int b)
      {
           a = b;
           cout << "调用构造函数:" << a << endl;
      }
      ​
      Data::~Data()
      {
           cout << "调用析构函数,a="< 
      

      调用顺序:
      1.D0默认构造函数。
      2.D1构造函数。
      3.D2构造函数。
      4.D3构造函数。
      5.此时遇到},自动对{}里的内容调用析构函数。
      6.D4构造函数。
      7.遇到},从下往上依次对D4,D2,D1,D0调用析构函数。

      总结:1.不加参时自动调用默认构造函数;2.遇到}自动调用析构函数;3.析构顺序为从下往上(最先构造的最后被析构).

      5.拷贝构造函数

      • 定义:

        拷贝构造函数 copy constructor 用一个已经存在的对象创建一个新的对象

        特点:

        1.如果没有实现拷贝构造函数,编译器自动生成一个拷贝构造函数(行为是bit-wise copy) 2.自定义拷贝构造函数:当编译器提供的拷贝构造函数无法满足需求。(如年龄大两岁)

      • 什么时候调用?

        新对象被旧对象初始化时。

      • 拷贝构造 和 无参构造 有参构造的关系

        如果用户定义了 拷贝构造或者有参构造 都会屏蔽无参构造。

        如果用户定义了 无参构造或者有参构造 不会屏蔽拷贝构造。

      • 拷贝构造几种调用形式

        1、旧对象给新对象初始化 调用拷贝构造。

        2、给对象取别名 不会调用拷贝构造。

        3、普通对象作为函数参数,调用函数时 会发生拷贝构造。

        4、函数返回值普通对象。

        Visual Studio会发生拷贝构造

        #include
        using namespace std;
        ​
        //创建一个类
        class Data
        {
        public:
             int a;
        public:
             Data();
             Data(int b);
             ~Data();
             Data(const Data& D);
        };
        //默认构造函数
        Data::Data()
        {
             a = 999;
             cout << "调用默认构造函数:" << a << endl;
        }
        //构造函数
        Data::Data(int b)
        {
             a = b;
             cout << "调用构造函数:" << a << endl;
        }
        //析构函数
        Data::~Data()
        {
             cout << "调用析构函数,a="< 

        运行结果:

      •  

      6.浅拷贝和深拷贝

      • 什么是浅拷贝和深拷贝?

        • 浅拷贝:只是数值一样。如果成员是指针,则原函数和拷贝构造函数公用一个地址。

        • 深拷贝:为拷贝函数的指针成员开辟新地址。

      • 分别在什么时候用浅拷贝和深拷贝?

        • 默认的拷贝构造 都是浅拷贝。

        • 如果类中没有指针成员, 不用实现拷贝构造和析构函数。

        • 如果类中有指针成员,且指向堆区空间, 必须实现析构函数释放指针成员指向的堆区空间,必须实现拷贝构造完成深拷贝动作(否则指针区域会被析构多次)。

      7.初始化列表

      • 对象成员

        定义:类中的成员也可以是对象,叫做对象成员。

        当一个类中有对象成员,无参调用时,不需要初始化列表。

        #include
        using namespace std;
        class A
        {
        public:
            int mA;
        public:
            A()
            {
                mA = 0;
                cout << "A类无参构造" << endl;
            }
            A(int a)
            {
                mA = a;
                cout << "A的有参构造" << endl;
            }
            ~A()
            {
                cout << "A的析构函数" << endl;
            }
        };
        class B
        {
        public:
            int mB;
            A ob;//对象成员
        public:
            B()
            {
                cout << "B类的无参构造" << endl;
            }
            ~B()
            {
                cout << "B的析构函数" << endl;
            }
        };
        int main(int argc, char* argv[])
        {
            B ob1;
            return 0;
        }

        调用顺序:

      •  

      • 调用有参构造函数时需要初始化列表

        #include
        using namespace std;
        class A
        {
        public:
            int mA;
        public:
            A()
            {
                mA = 0;
                cout << "A类无参构造" << endl;
            }
            A(int a)
            {
                mA = a;
                cout << "A的有参构造" << endl;
            }
            ~A()
            {
                cout << "A的析构函数" << endl;
            }
        };
        class B
        {
        public:
            int mB;
            A ob;//对象成员
        public:
            B()
            {
                cout << "B类的无参构造" << endl;
            }
            //ob(a)隐式调用有参构造
            //
            B(int a, int b):ob(a)
            {
                mB = b;
                cout << "B类的有参构造" << endl;
            }
            ~B()
            {
                cout << "B的析构函数" << endl;
            }
        };
        int main(int argc, char* argv[])
        {
            B ob1(10, 20);
            cout << "mA =" << ob1.ob.mA << ", mB =" << ob1.mB << endl;
            return 0;
        }

        调用顺序:

         

      8.explicit防止构造函数隐式转换

      explicit:声明为explicit的构造函数不能在隐式转换中使用

      //无explicit
      #include
      using namespace std;
      class MyString {
      public:
           MyString(int n) {
                cout << "MyString int" << endl;
           }
           MyString(const char* s) {
                cout << "MyString char" << endl;
           }
      };
      int main()
      {
           MyString str1 = 1;//这样赋值容易产生歧义,是数值赋值?还是类初始化?
           MyString str2(1);//正常调用
           return 0;
      }
      #include
      using namespace std;
      class MyString {
      public:
           explicit MyString(int n) {
                cout << "MyString int" << endl;
           }
           MyString(const char* s) {
                cout << "MyString char" << endl;
           }
      };
      int main()
      {
           //如果有explicit,则无法这样赋值
           //MyString str1 = 1;
           MyString str2(1);//正常调用
           return 0;
      }

      9.对象数组

      本质是数组,数组的每个元素是对象。

      #include
      using namespace std;
      class A {
      public:
           int ma;
           A() {
                ma = 0;
                cout << "A():" << ma << endl;
           }
           A(int a) {
                ma = a;
                cout << "A(int a):" << ma << endl;
           }
           ~A()
           {
                cout << "~A:" << ma << endl;
           }
      };
      int main()
      {
           //对象数组每个元素都会调用构造函数和析构函数
           //对象数组不初始化,每个元素调用无参构造
           A arr1[5];
      ​
           //对象元素初始化,必须调用显示有参构造,逐个初始化
           A arr2[3]={A(10),A(20),A(30)};//必须用{}
           cout <<"sizeof(arr2):"<< sizeof(arr2) << " " <<"sizeof(arr2[0]):" << sizeof(arr2[0]) << endl;
           int n = sizeof(arr2) / sizeof(arr2[0]);
           for (int i = 0; i < n; i++)
           {
                cout << arr2[i].ma << " ";
           }
           cout<< endl;
           return 0;
      }

       

      10.动态对象

      10.1 动态对象创建

      好处:可根据需要分配空间。

      具体操作:不用malloc,而用new创建动态对象,用delete释放动态对象。

      malloc的问题:
      1.必须直到对象长度。
      2.需要强制转换返回值。
      3.可能内存分配失败。
      4.构造函数中,不能自动调用初始化函数。
      new流程:
      自动分配内存+初始化。
      deletel:
      自动调用析构函数+释放内存。
      #include
      using namespace std;
      class Person {
      public:
           char* name;
           int age;
      public:
           //无参构造
           Person() {
                cout << "Person()" << endl;
                name = new char[sizeof("#")+1];
                strcpy(name, "#");
                age = 0;
           }
           //有参构造
           Person(const char* na, int ag) {
                cout << "Person(char,int)" << endl;
                name = new char[strlen(na) + 1];
                strcpy(name, na);
                age = ag;
           }
           void print() {
                cout << "name:" << name << " " << "age:" << age << endl;
           }
           ~Person() {
                cout << "~Person" << endl;
                if (name != NULL) {
                     delete[]name;
                     name = NULL;
                }
           }
      };
      int main()
      {
           Person *p1 = new Person;
           Person *p2 = new Person("YY~", 18);
           p1->print();
           p2->print();
           delete p1;
           delete p2;
           return 0;
      }

       

      10.2 动态对象数组

      对象数组中每个对象都要调用构造函数。

      除了栈上可以聚合初始化,其他情况必须提供一个默认的构造函数。

      #include
      using namespace std;
      class Person {
      public:
           char* name;
           int age;
      public:
           Person() {
                cout << "Person()" << endl;
                name = NULL;
                age = 0;
           }
           Person(const char* na, int ag) {
                cout << "Person(char,int)" << endl;
                name = new char[strlen(na) + 1];
                strcpy(name, na);
                age = ag;
           }
           ~Person() {
                cout << "~Person()" << endl;
                if (name != NULL) {
                     delete[]name;
                }
           }
      };
      //栈聚合
      void stack() {
           //栈聚合初始化
           Person person[] = { Person("Y",19),Person("ZZYY",99) };
           for (int i = 0; i < 2; i++)
           {
                cout << person[i].name << " " << person[i].age << endl;
           }
      ​
           //创建堆上对象数组必须提供构造函数
           Person* all = new Person[5];
           delete[]all;
      }
      int main()
      {
           stack();
           return 0;
      }

      11.静态成员

      不管类有多少对象,静态成员只有一个拷贝(副本),且这个拷贝 被类中所有对象共享。

      11.1 静态成员变量

      static修饰的静态成员 属于类,而不是具体的对象。

      所有对象共享同一个静态成员。

      • 注意!!!!

        static修饰的成员 定义类的时候 必须分配空间。

        static修饰的静态成员数据 必须类中定义 并且 类外初始化。

        #include
        using namespace std;
        class Data {
        public:
             int a;//普通成员
             //类中定义!
             static int b;//静态数据
        };
        ​
        //类外初始化!
        int Data::b = 99;
        ​
        void test()
        {
             //静态成员两种访问方式:
             //1.静态成员 可以通过类名称直接访问(属于类)
             cout <<" Data::b :"<< Data::b << endl;
             //2.静态成员 也可通过对象访问(共享)
             Data ob1;
             cout << "ob1.b :"<

         

      11.2 静态成员函数

      静态成员函数 属于类 而不是对象(所有对象 共享)

      • 定义:

        class Data{
             static void func(){
                  
             }
        }
      • 注!!!!

        静态成员函数可直接通过类名访问。

        静态函数内 只能操作 静态对象。

        class Data{
        private:
             int data;
             static int a;
        public:
             static int getA()
             {
                  data=10;//error 非静态对象,不能在静态数组里操作
                  return a;
             }
        }
        int Data::a=99;//静态成员类外定义
        void test(){
             cout< 

  • 相关阅读:
    margin塌陷和margin重合问题的解决方法总结
    机器学习(八) ----------支持向量积(SVM)
    【python】(十四)python内置库——json
    Python | Leetcode Python题解之第522题最长特殊序列II
    javaScript 的 this 究竟是个什么鬼?
    AI视频剪辑:批量智剪技巧大揭秘
    自定义GPT已经出现,并将影响人工智能的一切,做好被挑战的准备了吗?
    cpu设计和实现(基础)
    PCL 滤波采样(二)——MLS平滑
    数字信号处理-4-三角函数合成与傅里叶级数
  • 原文地址:https://blog.csdn.net/m0_61628700/article/details/127930064