类的界说及访问限定符
类的格式:
- class为类的关键字,class + 类的名字(自界说)+ {} + ;
- 类中可以界说成员变量也可以界说成员函数,在类中界说的成员函数默认是内联函数,变量为类的属性,函数为类的方法
- 类的成员在界说时可加上一些特别的标识符,便于区分,也可不加
例:
- #include <iostream>
- using std::cout;
- using std::endl;
- //用类来定义一个栈
- class stack // stack为类的名字
- {
- //类的成员函数
- void Init(int capacity = 10)
- {
- _arr = (int*)malloc(sizeof(int) * capacity);
- if(_arr == nullptr)
- {
- perror("malloc fail!");
- exit(-1);
- }
- _capacity = capacity; //有了特殊标识符就可区分,哪个是成员变量,哪个是形参了
- _top = 0;
- }
- //类的成员变量
- int* _arr; // "_"为特殊标识符
- int _capacity;
- int _top;
- };
- int main()
- {
- //...
- return 0;
- }
复制代码 类与布局体:
从上面的代码可以看到,类的格式与C语言中的布局体相似,但是C语言中的布局体成员只答应有变量,而不能有函数,以是C++对C的布局体举行了“升级”,在C++中,也可以用布局体来界说类
通过上面的代码,C++也可以用布局体界说类,但更多的时间,各人还是选择用class来界说
类的访问与访问限定符的关系:
- 对类举行访问时,首先要界说类的对象,对象其实就是C语言中的变量
- 界说的对象的范例就是自界说的类名
- 访问类的成员与访问布局体的成员是一样的,用到“ . ”操作符或者“->”操作符
- class A
- {
- void Print()
- {
- cout << "hello world!" << endl;
- }
- int _a;
- };
- int main()
- {
- A a; // a为类定义出来的对象,类名就是该对象的类型,类和对象也由此体现了出来
- a.Print(); // 通过“.”操作符来访问类的成员函数
- return 0;
- }
复制代码 接着来看,若将该代码放到当地来运行,结果如下:
出现如许的错误与访问限定符有关,介绍一下访问限定符:
这三个就是访问限定符,作用在类中,public表示可直接对类举行访问,private 和 protected 表示不可直接对类举行访问;限定符的作用域是从该访问限定符开始到下一个访问限定符出现为止,或者没有访问限定符的出现,直接到类的作用域结束;class在没有界说访问限定符时,默认是private,而struct默认是public,以是这也是为什么上面的代码会出现访问错误的原因,上面的成员函数和成员变量被作用在private中,不可直接对其举行访问;一般的,成员变量被设在private 和protected 中,而需要给别人使用的成员函数就被设为protected
类域
类域界说了一个新的作用域,类的成员变量和成员函数都放在作用域中,与命名空间的访问类似,在类体外访问域里的成员时,需要用到“: :”域作用限定符,指明要访问的成员属于哪个类域,类域影响的是编译的查找规则
如上图所示,在不同的两个及以上的文件中,界说类的成员函数时,就需用到域作用限定符,防止在同一个文件下出现两个同名函数时,界说的时间产生矛盾。如,在stack.h中再界说一个队列的类,类中的初始化成员函数名也是Init时,在.cpp文件中界说时,没有明白是哪个类域的初始化,就会产生矛盾,界说成员变量时,亦云云
类的实例化
概念:用类的范例创建对象的过程,就叫作类的实例化
- include <iostream>
- using std::cout;
- using std::endl;
- class A
- {
- public:
- void Print(int x)
- {
- _a = x;
- cout << _a << endl;
- }
- private:
- int _a;
- };
- int main()
- {
- // 这就是实例化出对象
- // 用类的类型A创建出来的对象a1,a2,a3
- A a1;
- A a2;
- A a3;
- return 0;
- }
复制代码 类相当于一个模型一样的东西,限定了类中有哪些成员变量,这写成员变量只是声明,并没有分配空间,只有用类实例化出对象时,才会分配空间(注:没有分配空间的是声明,分配了空间的才是界说),打个比方,创建出来的类相当于一张建房子要用到的图纸,图纸上有房子的整体外观,房子面积多大,高是多少,各种各样的数据,然后通过图纸来建房,建出来的房子就是通过图纸实例化出来的。类的成员变量没有空间,它就相当于图纸,图纸里的房子只是模板,不能住人,而只有实例化出来的对象才需要分配空间,通过图纸建出来的房子才可以住人,才需要空间。
对象的大小
通过上面的打印结果可知:
- 对象的大小只考虑成员变量,不考虑成员函数
- 对象的大小遵循内存对齐的规则
为什么不考虑成员函数?
对象的大小只考虑成员变量,是因为每个对象所要存储的数据不同,以是要为每个变量开辟空间,来存储数据,而函数在被界说编译时,就已经确定了,编译完之后会生成一条条指令,第一条指令就是函数的地址,而每个对象在调用函数时,都会通过函数的地址举行调用,以是每个对象所调用的函数都是同一个函数,若每个对象中都存储一个相同的函数,就显得多余了,以是系统会将函数放到一个公共的地域,每个对象需要调用时,就可直接调用
从图中可以看到,d1和d2同时调用Init函数,call指令就是进入函数的指令,且两个对象的call指令的地址都相同
为什么要内存对齐?
以是由上图可知,内存对齐是为了提高读取数据的速率
this指针
从上面的图片中看到,不同的对象调用的函数是相同的,那为什么打印出来的数据却不同呢?出现如许的结果是因为this指针在里面起到了作用
- 编译器编译后,默认会在类的成员函数的形参的第一个位置增加一个当前范例的this指针
- class date
- {
- public:
- void Init(int year, int month, int day)
- // 本质:void Init(date* const this,int year, int month, int day)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- void Print()
- // 本质:void Print(date* const this)
- {
- cout << _year << "/" << _month << "/" << _day << endl;
- // 本质:cout << this->_year << "/" << this->_month << "/" << this->_day << endl;
- }
- private:
- int _year;
- int _month;
- int _day;
- };
- int main()
- {
- date d1;
- date d2;
-
- d1.Init(2024, 12, 19); //本质:d1.Init(&d1,2024,12,19);
- d2.Init(2024, 8, 8);//本质:d2.Init(&d2,2024,8,8);
- d1.Print(); // 本质:d1.Print(&d1);
- d2.Print();// 本质:d2.Print(&d1);
- return 0;
- }
复制代码 正因为有了this指针,以是才能做到打印出来的数据不同
- 在类的成员函数中访问成员变量时,本质上都是通过this指针来举行访问的
从上面的代码就了知道了,但大多数环境下,this指针是不会被写出来的
- C++规定不能在实参和形参的位置表现地写this指针,但在成员函数体中可表现
可看到表现this指针之后出现了错误,但在函数体中是可以表现的
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |