• 类和对象(1)


    1.面向过程和面向对象初步认识

    C++面向对象但不纯面向对象。JAVA纯面向对象。
    所以C++支持C和C++面向对象混编。
    C面向过程,关注的是过程

    2.类的引入

    类和结构体的区别:

    1. 类里可以有数据:成员变量
    2. 类可以定义函数
    3. 可以写成ListNode,不需加struct。
      C struct ListNode是类型。
      C
    //C++兼容C结构体的用法。
    typedef struct ListNode
    {
      int val;
      struct ListNode* next;
    }LTN;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    C++

    //C++把结构体升级成类
    struct ListNode//声明这是个类,struct关键字不能去掉
    {
      int val;
      ListNode* next;//后面想用这个类型的时候可以去掉。
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    如何用类?
    C数据和方法分离,C++方法可以在类的里面

    struct Stack
    {
    //成员函数
    //成员函数直接定义到类里面
    	void Init(int n=4)//缺省值
    	{
    		a = (int*)malloc(sizeof(int) * n);
    		if (nullptr == a)
    		{
    			perror("malloc申请空间失败");
    			return;
    		}
    		capacity = n;
    		size = 0;
    	}
    	void Push(int x)
    	{
    		//...
    		a[size++] = x;
    	}
    	//成员变量
    	int* a;
    	int size;
    	int capacity;
    };
    int main()
    {
    	Stack st;//对象
    	//如何调用函数?
    	st.Init(4);
    	st.Push(1);
    	st.Push(2);
    	st.Push(3);
    	st.Push(4);
    	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

    C++可以用struct定义类,但更喜欢用class定义类。

    3.类的定义

    class className
    {
      //类体:包括成员函数(类的方法)和成员变量(类的属性)
    }//一定要注意后面的分号!!!!
    
    • 1
    • 2
    • 3
    • 4
    • class:定义类的关键字
    • className:类名
    • {}:类的主体
    • 类体中的内容:类的成员
      定义一个声明和定义分离的类。
      缺省参数生命和定义不能同时给,一般在声明给。
      Stack.h
    #pragma once
    #include 
    #include 
    //类成员函数声明和定义分离
    struct Stack
    {
    	//成员函数
    	void Init(int n = 4);//缺省参数在声明给
    
    	void Push(int x);//类的声明
    	//成员变量
    	int* a;
    	int size;
    	int capacity;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Stack.cpp

    #include "Stack.h"
    void Stack::Init(int n)//Stack告诉编译器Init不是全局函数,是栈这个类的成员函数
    {
    	a = (int*)malloc(sizeof(int) * n);//看栈这个类里有没有a这个成员变量
    	if (nullptr == a)
    	{
    		perror("malloc申请空间失败");
    		return;
    	}
    	capacity = n;
    	size = 0;
    }
    void Stack::Push(int x)
    {
    	//...
    	a[size++] = x;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    用class Stack却编不过,为什么呢?
    此时要用到访问限定符。

    4.类的访问限定符和封装

    4.1访问限定符

    C++访问限定符有三种:公有保护和私有,现阶段保护和私有没有区别。共有及在类外可以直接访问。保护和私有在类里可以访问,类外不可访问。

    公有保护私有
    publicprotectprivate
    1. 访问限定符不会限定在类里面的访问,锁外面的人不锁里面的人。
    2. 类里面可以有多个访问限定符,限定从该访问限定符到下一个访问限定符出现时位置,如果没有下一个访问限定符则到}结束。
    3. class的默认访问限定符是私有,struct是公有(因为struct要兼容C)。所以上一文中报错了。
      实际生活中不希望默认,希望大家指清楚到底是私有还是公有。
      注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别。
    #pragma once
    #include 
    #include 
    struct Stack
    {
    publicvoid Init(int n = 4);
    	void Push(int x);
    privateint* a;
    	int size;
    	int capacity;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    大多数情况下成员变量都是私有的,成员函数不给别人用的是私有,给别人用的是公有。
    C++中struct和class没有区别,但是struct可以像C语言去用。
    Test.cpp

    class Date
    {
    public:
    	void Init(int year, int month, int day)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    
    };
    int main()
    {
    	Stack st;
    
    	Date d1;
    	d1.Init(2023, 2, 3);//调用函数,年月日被初始化
    	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

    4.2封装

    【面试题】:面向对象的三大特性:封装,继承,多态
    封装:将下面的细节藏起来,本质是一种更好的管理。
    C++的封装:将数据和方法都放在类里面去了,即当前封装的极限,并把自己想访问的定义成共有,不想的定义成私有和保护。

    5.类的作用域

    类定义出一个新的作用域(类域)。

    class Person
    {
     public:
       void PrintPersonInfo();
     private:
       char _name[20];
       char _gender[3];
       int _age;
    };
    void Person::PrintPersonInfo()//PrintPersonInfo属于Person这个类域
    {
      cout<<_name<<" "<<_gender<<" "<<_age<<endl;//此时可以调用私有
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    6.类的实例化

    即用类类型
    声明即要定义这个函数或变量,这个变量的类型是什么,名称是什么,参数是什么,但实际这个变量没有出来。定义即把其实实在在在的空间给开出来(对于变量而言)。
    类就像一个别墅的设计图,设计了细节,但不能住人,实例化即根据设计图建造出一栋栋别墅。
    在这里插入图片描述

    class Date
    {
    public:
    	void Init(int year, int month, int day)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}//函数的定义
    private:
    	int _year;
    	int _month;
    	int _day;//声明,没有开空间
    
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    只需要计算成员变量的大小。

    int main()
    {
    
    	//类对象实例化--开空间
    	Date d1;//定义才是开空间,对于对象整体定义。
    	//Date._year=1;
    	//Date::year=0;
    	//以上两种不可以,声明内不可存数据。
    	//d1._year=1;//也不行,访问限定符是私有的。访问不了,访问的时候需要调用这个函数。改成公有就可以了。
    	d1.Init(2023,9,12);//Init函数存在哪里呢?
    	d1._year++;
    	cout<<sizeof(d1)<<endl;
    	//类对象大小要考虑内存对齐规则
    	return 0;
    }//输出12
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    为什么成员变量存在对象里面,成员函数不存在对象里面?
    每个对象的成员变量不一样,需要独立存储。
    每个对象调用的成员函数一样,放到共享公共区域(代码段)。
    只需要计算成员变量的大小。

    //类中既有成员变量,又有成员函数
    class A1{
    pubic:
     void f1(){}
    private:
     int a;
    };//sizeof(A1):4
    //类中仅有成员函数
    class A2{
    public:
     void f2(){}
    };//sizeof(A2):1//【考点】如果是0,A2 aa1;没有实例化,不能取地址
    //类中什么也没有
    class A3
    {}//sizeof(A3):1,这一个字节用来进行占位,不存储有效数据,标识对象存在过,被实例化定义出来了。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    没有成员变量的类都是一个字节

    6.2结构体内存对齐规则

    1. 第一个成员在于结构体偏移量为0的地址处
    2. 其他成员变量要对齐到某个数字(对其书)的整倍的地址处
      注意:对齐数=编译器默认的一个对齐数与该成员大小的较小值。VS默认对齐数为8.
    3. 结构体的总大小:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
    4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

    7.this指针

    原来代码:

    class Date
    {
    public:
    	void Init(int year, int month, int day)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    编译器处理完:

    class Date
    {
    public:
    		void Init(Date*this,int year, int month, int day)
    	{
    		this->_year = year;
    		this->_month = month;
    		this->_day = day;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    原来代码:

    int main()
    {
    	Date d1;
    	Date d2;
    	d1.Init(2023, 2, 3);
    	d2.Init(2022, 2, 3);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    编译器处理完:

    int main()
    {
    	Date d1;
    	Date d2;
    	d1.Init(&d1,2023, 2, 3);
    	d2.Init(&d2,2022, 2, 3);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如果是d1调用,this是d1的地址,赋值给d1的年月日。
    不可以自己去加。

    //可以在类里使用this,但是实参和形参里不可以
    cout<<this<<endl;
    this->_year = year;
    this->_month = month;
    this->_day = day;
    //一般不会这么写
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. this存在哪里?–存在对象里面?答案❌。
      在栈上,因为他是形参,隐含的形参,不需要显示写,是编译器自己加的。/VS下通过ecx寄存器。
      程序进行编译,编译后成员变量存在对象里,实例化出一个对象,对象存在上。成员函数不要存在栈中,因为成员函数存在一个公共区域,编译的时候要确定call这个函数的地址,这个地址不在对象中去找,在代码段。因为函数的地址是这些指令的地址,这些指令是存在哪呢?存在于代码段。两个东西完全不一样,要从两个不同的角度理解。一个是指令,一个是指令运行过程中的相关数据。不要把两个东西混在一起。
      《深入理解计算机系统》——修炼内功

    7.2this指针的特性

    void func()
    {
     cout<<this<<endl;
     cou<<"func()"<<endl;
     }
    int main()
    {
    //编译报错 运行崩溃 正常运行
      Date* ptr=nullptr;
      ptr->func();
      //结果:正常运行
      ptr->Init(2023,9,12);//运行崩溃,用this去解引用了this->year
      ptr->_year;//会崩溃,因为_year在对象里面,到指针指向的对象去找,指针是一个空指针,相当于解引用。*ptr).func();//正常运行,ptr真正的意义是传递给this,所以也是正常运行,没有解引用这个行为
      Date::func();//不能这么调用,因为要传递this指针。没有this指针调用。
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    指针调用用箭头,有箭头不一定解引用。函数不在对象里。调用这个函数要call这个地址,这个地址在公共区域去找,公共区域:代码段。
    成员函数不可以不用对象去调,直接func()。

    1. 受到类域的限制,一般都不在类域里面去找,只在全局去找。在全局找func找不到
    2. 告诉func是Date的成员函数,调用成员函数要传递this指针,所以没有解引用,但是ptr传递给了this指针(cout<
    3. 会不会解引用,取决于要不要在对象中去找,而不是有没有这个符号。
      调用函数一共就需要两个动作,一个是传参,传this指针,一个是调用函数,这两个动作都不涉及要去解引用。
      成员函数不是成员对象。

    封装(补充)

    C语言和C++真正的区别:数据结构的实现与语言无关

    CC++
    数据和方法分离的都封装在类里面
    名字繁琐简洁
    数据访问自由不受限制控制访问方式。愿意给你访问的公有,不愿意私有
    底层一样

    封装在一起才能通过访问限定符限制。

  • 相关阅读:
    简单三步,让你的二维码焕发新生
    实施 ECM 系统的 6 大挑战——以及如何避免它们
    2023-10-19 node.js-将异步处理修改为同步-使用Promise和async-记录
    Linux——mysql主从复制配置
    css背景图片的相关知识
    Allegro如何打盲埋孔操作指导
    KylinOSv10系统k8s集群启动mysql5.7占用内存高的问题
    简单SQL与单行函数
    html、js、css3制作一款辉光管时钟
    一篇文章教你如何在项目中正确使用@DateTimeFormat注解和@JsonFormat注解
  • 原文地址:https://blog.csdn.net/weixin_73236117/article/details/132815825