• 8.3 C++ 定义并使用类


    C/C++语言是一种通用的编程语言,具有高效、灵活和可移植等特点。C语言主要用于系统编程,如操作系统、编译器、数据库等;C语言是C语言的扩展,增加了面向对象编程的特性,适用于大型软件系统、图形用户界面、嵌入式系统等。C/C++语言具有很高的效率和控制能力,但也需要开发人员自行管理内存等底层资源,对于初学者来说可能会有一定的难度。

    使用结构体定义类: 其实使用C语言中的结构体类型,我们可以模拟出一个伪类,虽然很麻烦,但是也凑活着能用.

    #include 
    
    using namespace std;
    
    struct Student
    {
      char name[64];
      int age;
    };
    
    void Print_Student(struct Student *ptr)
    {
      printf("Name: %s --> Age: %d \n", ptr->name, ptr->age);
    }
    
    int main(int argc, char *argv[])
    {
      struct Student stu;
    
      strcpy(stu.name, "lyshark");
      stu.age = 23;
    
      Print_Student(&stu);
    
      system("pause");
      return 0;
    }
    
    • 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

    定义简单的类: 接着我们来定义一个真正的Student类,并调用成员函数实现对数据成员的输出.

    #include 
    #include 
    
    using namespace std;
    
    class Student
    {
    public:
      int uid;
      char *name;
      int age;
    
    public:
      void set(int id, char *name, int age)
      {
        this->uid = id;
        this->name = name;
        this->age = age;
      }
      void display()
      {
        cout << this->uid << this->name << this->age << endl;
      }
    };
    
    void Call(const Student &ptr)
    {
      cout << ptr.name << endl;
    }
    
    int main(int argc, char *argv[])
    {
      Student stu;                                       // 实例化
    
      stu.set(1001, "lyshark", 23);                     // 设置数据
      cout << stu.uid << stu.name << stu.age << endl;   // 输出
      stu.display();                                    // 使用成员函数输出
    
      Student *ptr = &stu;                              // 使用指针
      cout << ptr->uid << ptr->name << ptr->age << endl;
    
      Call(stu);
      system("pause");
      return 0;
    }
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    定义构造/析构函数: 构造函数通常用于初始化类中的数据成员,析构函数则主要负责对类的清理工作.

    #include 
    #include 
    
    using namespace std;
    
    class Student
    {
    public:
      int uid;
      char *name;
      int age;
    
    public:
      Student() { cout << "init .." << endl; }  // 定义无参构造函数
    
      Student(int uid, char *name, int age)     // 定义有参构造函数
      {
        this->uid = uid;
        this->name = name;
        this->age = age;
      }
      ~Student()                               // 定义析构函数
      {
        cout << "执行结束,析构 !" << endl;
      }
      void Display()
      {
        cout << this->name << endl;
      }
    };
    
    int main(int argc, char *argv[])
    {
      class Student *stu_ptr[3];
    
      Student stu1(1001, "admin", 22);
      Student stu2(1002, "guest", 33);
      Student stu3(1003, "tudyit", 25);
    
      stu_ptr[0] = &stu1;
      stu_ptr[1] = &stu2;
      stu_ptr[2] = &stu3;
    
      for (int x = 0; x < 3; x++)
        stu_ptr[x]->Display();
    
      system("pause");
      return 0;
    }
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    定义拷贝构造函数: 拷贝构造函数的主要作用就是实现两个类之间的数据成员的克隆.

    #include 
    #include 
    
    using namespace std;
    
    class Student
    {
    public:
      int uid;
      char *name;
      int age;
    
    public:
      Student(int uid, char *name, int age)     // 构造函数
      {
        this->uid = uid;
        this->name = name;
        this->age = age;
      }
      Student(const Student& ptr)             // 拷贝构造函数
      {
        this->uid = ptr.uid;                // 实现将传入的参数
        this->name = ptr.name;              // 拷贝到新的类中
        this->age = ptr.age;                // 此时两个类就具有相同属性了
        cout << &ptr << " clone" << endl;
      }
    };
    
    int main(int argc, char *argv[])
    {
      Student stu1(1001, "admin", 22);
    
      // 如下开辟新对象stu2 -> 将stu1中的数据拷贝到stu2
      Student stu2(stu1);
      cout << stu2.name << endl;
      cout << stu2.age << endl;
    
      // 第二种拷贝方式
      Student stu3 = Student(stu1);
      cout << stu3.name << endl;
    
      system("pause");
      return 0;
    }
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    使用浅/深拷贝: 浅拷贝主要是对数据成员表层的拷贝,这种拷贝容易出现问题,而深拷贝则是完全对数据的复制.

    先来看下面这段代码,该代码就是一个典型的浅拷贝,当代码Student stu2(stu1);拷贝完成后,在执行析构函数时,由于是浅拷贝,数据成员共用一片内存区域,而析构函数则执行了两次,第一次释放后,第二次释放内存则出现冲突的情况.

    #include 
    
    using namespace std;
    
    class Student
    {
    public:
      char * m_name;
      int m_age;
    
    public:
      Student(char * name, int age)
      {
        this->m_age = age;
        this->m_name = (char *)malloc(strlen(name) + 1);
        strcpy(this->m_name, name);
      }
      ~Student()
      {
        if (m_name != NULL)
          free(m_name);
      }
    };
    
    int main(int argc, char *argv[])
    {
      Student stu1("lyshark", 25);  // 定义数据
      Student stu2(stu1);           // 调用拷贝构造函数
    
      system("pause");
      return 0;
    }
    
    • 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
    • 32

    接着看下面的深拷贝代码,我们通过自己开辟堆空间,然后自己在拷贝构造函数中拷贝数据,防止冲突,同样的代码经过简单的的修改,就可以避免拷贝是数据释放的冲突问题.

    #include 
    
    using namespace std;
    
    class Student
    {
    public:
      char * m_name;
      int m_age;
    
    public:
      Student() { }
      Student(char * name, int age)
      {
        this->m_age = age;
        this->m_name = (char *)malloc(strlen(name) + 1);
        strcpy(this->m_name, name);
      }
      ~Student()
      {
        if (m_name != NULL)
          free(m_name);
      }
      Student(const Student &ptr)        // 定义拷贝构造函数
      {
        this->m_age = ptr.m_age;
        this->m_name = (char *)malloc(strlen(ptr.m_name) + 1);
        strcpy(this->m_name, ptr.m_name);  // 深层拷贝
      }
    };
    
    int main(int argc, char *argv[])
    {
      Student stu1("lyshark", 25);
      Student stu2(stu1);
    
      system("pause");
      return 0;
    }
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    构造函数初始化列表: 定义构造函数也可使用初始化列表的形式来对数据赋值,这种方式很简洁,适合简单的构造函数使用.

    #include 
    
    using namespace std;
    
    class Student
    {
    public:
      char * m_name;
      int m_age;
    
    public:
      // 无参数构造函数的写法
      Student(){ }
      // 有参构造函数,使用初始化列表
      Student(char * x, int y) :m_name(x), m_age(y){}
      // 普通函数也可写成一行
      void Show(){ cout << this->m_name << endl; }
    };
    
    int main(int argc, char *argv[])
    {
      Student stu1("lyshark",24);
      stu1.Show();
    
      system("pause");
      return 0;
    }
    
    • 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

    new 动态对象创建: 该关键字可用来实例化类,它可以自动分配初始化空间,开辟到堆中,并且自动来维护释放,非常方便.

    #include 
    
    using namespace std;
    
    class Student
    {
    public:
      char * m_Str;
    
    public:
      Student() { cout << "默认构造调用" << endl; }
      ~Student() { cout << "默认析构调用" << endl; }
    };
    
    int main(int argc, char *argv[])
    {
      // malloc 返回 void* 还必须要强转
      Student stu1;    // 在栈上开辟的空间
    
      // 所有new出来的对象都会返回该类型的指针,并且会调用默认构造函数
      Student *stu2 = new Student;   // 堆区开辟空间
      delete stu2;                   // 释放堆区空间
    
      // 使用new 来开辟数组,他一定会调用默认构造函数,有多少数组成员就调用多少次!
      Student *pArray = new Student[10];
    
      pArray[0].m_Str = "lyshark";
      cout << pArray[0].m_Str << endl;
      delete[] pArray;                 // 释放整个数组,必须加[]中括号
    
      system("pause");
      return 0;
    }
    
    • 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
    • 32
    • 33

    静态数据成员: 静态成员变量,无论建立多少个对象,都只有一个静态数据的拷贝,所有对象都共享这个静态数据.

    #include 
    
    using namespace std;
    
    class Student
    {
    public:
      // 静态成员变量必须在类内声明,在类外对其进行初始化
      static int m_number;    // 定义静态成员变量
      static char * m_name;   // 定义静态成员变量
    };
    
    int Student::m_number = 100;         // 类外初始化
    char *Student::m_name = "lyshark";   // 类外初始化静态成员变量
    
    int main(int argc, char *argv[])
    {
      Student stu1, stu2;
    
      // 此处如果对stu1中的静态变量修改后,stu2也会受到影响,两个是共享的数据源
      stu1.m_number = 200;
    
      cout << stu1.m_number << "   " << stu1.m_name << endl;
      cout << stu2.m_number << "   " << stu2.m_name << endl;
    
      cout << "通过类名直接访问:" << Student::m_number << endl;
      cout << "通过类名直接访问:" << Student::m_name << endl;
    
      system("pause");
      return 0;
    }
    
    • 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

    静态成员函数: 静态成员函数,不可以访问普通的成员变量,仅可以访问通过static修饰的,静态成员变量.

    #include 
    
    using namespace std;
    
    class Student
    {
    public:
      static int m_number;
    
    public:
      static void Display()
      {
        cout << m_number << endl;
        cout << "hello lyshark" << endl;
      }
    };
    
    int Student::m_number = 100;         // 类外初始化
    
    int main(int argc, char *argv[])
    {
      Student stu1, stu2;
    
      stu1.Display();  // 调用两个函数返回值相同
      stu2.Display();  // 两个成员函数就是一个
    
      system("pause");
      return 0;
    }
    
    • 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

    关于this指针: 该指针是默认存在于类中的,由编译器维护,this指针指向被调用的成员函数所属的对象.

    #include 
    #include 
    
    using namespace std;
    
    class Person
    {
    public:
      char *name;
      int age;
    
    public:
      Person(char *p_name, int p_age) { this->name = p_name; this->age = p_age; }
    
      void Compare_Age(Person & ptr)          // 对比年龄是否相等
      {
        if (this->age == ptr.age)
          cout << "Same age" << endl;     // 年龄相同
        else
          cout << "Same not age" << endl; // 年龄不同
      }
    
      void PlusAge(Person & ptr)              // 两个年龄相加
      {
        this->age += ptr.age;
      }
    
      Person & Push_Age(Person &ptr)
      {
        this->age += ptr.age;
        return *this;                       // 返回指向对象本体
      }
    
    };
    
    int main(int argc, char *argv[])
    {
      Person per1("lyshark", 33);
      Person per2("admin", 33);
    
      per1.Compare_Age(per2);      // 判断两个类年龄是否相等
    
      per1.PlusAge(per2);          // 将两个年龄相加
      cout << per1.age << endl;    // 输出年龄
    
      per1.Push_Age(per2).Push_Age(per2);  // 链式编程(必须传递引用)
      cout << per1.age << endl;
    
      system("pause");
      return 0;
    }
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    空指针访问成员函数: 如下定义空指针,并尝试使用空指针访问类,那么如果类中没有判断空指针的语句,则程序会崩溃.

    #include 
    
    using namespace std;
    
    class Person
    {
    public:
      int m_age;
    
    public:
      void show() { cout << "person show" << endl; }
      void show_age()   // 此处如果没有if判断,则程序会报错
      {
        if (this == NULL)   // 防止使用空指针访问
          return;
        cout << "show: " << m_age << endl;
      }
    };
    
    int main(int argc, char *argv[])
    {
      // show_age() 默认会加上 this --> 那么如果传递空指针则失败
      // 代码接收空指针,会溢出,说以在类中进行判断,可以防止空指针错误
    
      Person *ptr = NULL;
    
      ptr->show();          // 这个可以
      ptr->show_age();      // 不可以
    
      system("pause");
      return 0;
    }
    
    • 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
    • 32

    定义常函数/常对象: 使用Const修饰的成员函数,则是常函数,如果用来修饰对象则是常对象,两者均不可直接修改.

    #include 
    
    using namespace std;
    
    class Person
    {
    public: int m_x; int m_y;
    
    public:
      Person() { this->m_x = 0; this->m_y = 0; }  // 初始化构造函数
    
      void showInfo() const // 声明常函数,函数内部不可以修改指针的指向
      {
        // this->m_x = 1000; 相当于修改成了 --> const Person * const this
        // this->m_x = 200;  常函数无法直接修改
        cout << this->m_x << endl;
      }
    };
    
    int main(int argc, char *argv[])
    {
      // 常函数的调用
      Person per1;
      per1.showInfo();
    
      // 定义常对象,常对象不允许修改数据,只能调用
      const Person per2;
      per2.showInfo();
    
      system("pause");
      return 0;
    }
    
    • 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
    • 32

    友元函数的定义: 将全局函数定义为友元函数,让外部函数,可以访问特定的类内部的私有数据.

    #include 
    #include 
    
    using namespace std;
    
    class Student
    { // 定义友元函数 --> 让goodGay 可以访问我的私有属性
      friend void goodGay(Student *ptr);
    private:
      string m_badRoom;        // 私有的卧室
      string m_sittingRoom;    //  私有的客厅
    
    public:
      Student()
      {
        this->m_sittingRoom = "客厅";
        this->m_badRoom = "卧室";
      }
    };
    
    // 全局函数,我想让他能访问到私有的卧室
    void goodGay(Student *ptr)
    {
      cout << "访问我的客厅:" << ptr->m_badRoom << endl;
      cout << "访问我的卧室:" << ptr->m_sittingRoom << endl;
    }
    
    int main(int argc, char *argv[])
    {
      Student *stu = new Student;
      goodGay(stu);
    
      system("pause");
      return 0;
    }
    
    • 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
    • 32
    • 33
    • 34
    • 35

    友元类的定义: 我们除了可以让全局函数访问类中的特定私有数据外,也可以让一个对象实现相同的功能.

    #include 
    
    using namespace std;
    
    class Teacher
    {
      friend class Student;  // 让Student学生,可以访问我的私有成员
    
    private:
      char * m_school;     // 老师所在的学校
      char * m_class;      // 老师所教的班级
    
    public:
      Teacher()
      {
        this->m_school = "中心小学";
        this->m_class = "一年级二班";
      }
    };
    
    class Student
    {
    private: Teacher *ptr;              // 设置指向teacher的指针
    
    public:
      Student(){ ptr = new Teacher; } // 构造函数初始化
      void Display()
      {
        // 此时Student中的Display()函数可以访问Teacher类中的私有数据
        cout << "学生访问到的学校: " << this->ptr->m_school << endl;
        cout << "学生访问到的班级: " << this->ptr->m_class << endl;
      }
    };
    
    int main(int argc, char *argv[])
    {
      Student stu;
      stu.Display();
    
      system("pause");
      return 0;
    }
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    类实现单例模式: 所谓单例模式就是说一个类只能实例化出一个对象,不论你new创建多少次,始终让其保证只有一个对象,这样就可以防止冲突的情况发生,如下是打印机案例的演示,一个公司可能只有一个打印机,这个打印机必须要单一.

    #include 
    
    using namespace std;
    
    class Printer
    {
    private:
      static Printer * single_Print;
      static int count;
    
    private:
      Printer(){ };
      Printer(const Printer & ptr);
    
    public:
      static Printer * getInstance()
      {
        count = count + 1;
        return single_Print;
      }
      static void PrintText(char * text)
      {
        cout << "Printer ready: " << count << text << endl;
      }
    };
    
    // 初始化打印机中的静态数据成员
    Printer * Printer::single_Print = new Printer;
    int Printer::count = 0;
    
    int main(int argc, char *argv[])
    {
      // 拿到打印机对象指针,后期通过该指针操作数据
      Printer * ptr = Printer::getInstance();
    
      ptr->getInstance();
      ptr->getInstance();          // 每次调用都会+1
      ptr->PrintText(" count");    // 调用打印机
    
      system("pause");
      return 0;
    }
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
  • 相关阅读:
    freeswitch-1.10.7 on centos7编译安装
    【软件测试】--功能测试4-html介绍
    PrimalScript通用脚本环境
    父组件调用子组件 ref 不生效?组件暴露 ref ?
    TypeScript中的declare关键字
    【单片机毕业设计】【mcuclub-jj-002】基于单片机的三层电梯的设计
    搭建vue后台管理系统框架
    3--Linux:基础命令2
    ThreadLocal 源码浅析
    内核编译 --- 链接器
  • 原文地址:https://blog.csdn.net/lyshark_csdn/article/details/133961635