前言
今天呢小编想与各人分享C++关于类和对象的相干知识点。由于类和对象这个板块呢知识点比较多。所以 小编分为了上中下三篇来给各人分享。接下来就跟着小编进入类和对象的章节吧。
一、面向过程和面向对象开端认识.
这里我们结合C语言来与C++ 作比较:
C语言是面向过程的。关注的是过程。分析出求解问题的步骤。然后在通过函数的调用来渐渐解决问题
C++是基于面向对象的。关注的是对象,将一件事拆成不同的对象,靠对象 之间的交互完成问题。
二、类的定义
- class className
- {
- // 类体:由成员函数和成员变量组成
- }; // 一定要注意后面的分号
复制代码 class为定义类的关键字,ClassName为类的名字,{}中为类的主体,留意类定义竣事时后面分
号不能省略
类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。
类的两种定义方式:
- 声明和定义全部放在类体中,需留意:成员函数如果在类中定义,编译器可能会将其当成内
联函数处理。
- class Book//书
- {
- public:
- void Print()//显示书的相关特性
- {
- cout << _name << _price << _number << endl;
- }
- private:
- char _name;//书名
- int _price;//价格
- char _number;//编号
- };
复制代码
- 类声明放在.h文件中,成员函数定义放在.cpp文件中,留意:成员函数名前需要加类名::
- test.h文件
- using namespace std;
- class Book//书
- {
- public:
- void Print();//显示书的相关特性
- private:
- char _name;//书名
- int _price;//价格
- char _number;//编号
- };
- test.cpp文件
- #include"test.h"
- void Book::Print()
- {
- cout << _name << _price << _number << endl;
- }
复制代码 二、类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 ::
作用域操作符指明成员属于哪个类域。
- class Data//日期
- {
- public:
- void Dataday(int year, int month, int day)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- void DataPrint()
- {
- cout << _year << "/" << _month << "/" << _day << endl;
- }
- private:
- int _year;//年
- int _month;//月
- int _day;//日
- };
- class Data//日期
- {
- public:
- void Dataday(int year, int month, int day);
- void DataPrint();
- private:
- int _year;//年
- int _month;//月
- int _day;//日
- };
- void Data::Dataday(int year, int month, int day)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- void Data::DataPrint()
- {
- cout << _year << "/" << _month << "/" << _day << endl;
- }
复制代码 就像如许,如许实在跟我们之前的学的namespace命名空间一样的。都是重新定义了一个新的域,当我们在外边定义成员或者调用成员函数的时候,就需要用到作用域限定符(::)了,通过作用域限定符来找到要找的 成员 。
三、类的实例化
概念:用类类型创建对象的过程,称为类的实例化
- #include<iostream>
- using namespace std;
- class Data//日期
- {
- public:
- void Dataday(int year, int month, int day)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- void DataPrint()
- {
- cout << _year << "/" << _month << "/" << _day << endl;
- }
- private:
- int _year;//年
- int _month;//月
- int _day;//日
- };
- int main()
- {
- Data d1;
- d1.Dataday(2024,11,12);
- d1.DataPrint();
- return 0;
- }
复制代码 用类类型创建对象的过程,称为类的实例化。代码中的d1就是类的实列化,
留意:
- 类是对对象举行描述的,是一个模子一样的东西,限定了类有哪些成员,定义出一个类并没
有分配实际的内存空间来存储它;
- class Data
- {
- public:
- private:
- int _year;//年
- int _month;//月
- int _day;//日
- };
复制代码 既然类是堆对象的描述,不开辟新的空间。但是这里为什么要用到int呢?这个不是在定义了吗?既然定义了那就要开辟新的空间了呀?这里我们不能还是跟从前一样明白成定义了。这里它是声明,声明是不占用空间的。所以这里呀留意一下:
- 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
- int main()
- {
- Data d1;
- Data d2;
- d1.Dataday(2024,11,12);
- d1.DataPrint();
- d2.Dataday(2024, 11, 12);
- d2.DataPrint();
- return 0;
- }
复制代码 d1和d2就是类的实例化
如果这里直接要使用_year是会报错的。由于Data是没有空间的,只有Data实例化出来的对象才有空间。
打个比方:类的实例化出来的对象就好像我们在现实生存中使用构筑设计图建房子一样。然而类就是设计图,他只是设计出了要构筑的什么样的房子,但是在现实中是没有实物存在的。而实例化就是根据设计图构筑出来的实物,是具体存在的东西。同样类只是一个设计,实例化出来的对象才能实际储存数据,占用物理空间。
四、类的访问限定符及封装
1 .访问限定符
各人可以发现我们之前的代码内里出现的public和private是什么呢?它的意思就是跟他的英文意思一样的,是公共的和私有的意思,在C++中我们把他们叫做访问限定符固然另有一个叫做protected(保护)
访问限定符的阐明:
1.public修饰的成员在类外可以直接访问。
2.protected和private修饰的成员的在类外是不可以直接访问的(此处protected和private是类似的)。
3.访问权限作用域是从该访问限定符出现的位置到下一个访问限定符出现的位置为止
4.如果后面没有访问限定符,那作用域就到“}”竣事。
5.class默认为的是private(私有)的,struct则是public(共有),由于这里struct要兼容C。
留意:访问限定符只在编译时有效,当数据映射到内存后,没有任何访问限定符上的区别
2.封装
【口试题】面向对象的三大特性:封装、继承、多态。
在类和对象阶段,主要是研究类的封装特性,那什么是封装呢?
封装:将数据和操作数据的方法举行有机结合,隐蔽对象的属性和实现细节,仅对外公开接口来
和对象举行交互
封装本质上是一种管理,让用户更方便使用类。比如:对于电脑如许一个复杂的设备,提供给用
户的就只有开关机键、通过键盘输入,显示器,USB插孔等,让用户和计算机举行交互,完成日
常事务。但实际上电脑真正工作的却是CPU、显卡、内存等一些硬件元件。对于计算机使用者而言,不消关心内部核心部件,比如主板上线路是怎样结构的,CPU内部是怎样设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机举行交互即可。因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐蔽起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机举行交互即可
五、this指针
- #include<iostream>
- using namespace std;
- class Data//日期
- {
- public:
- void DataInit(int year, int month, int day)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- void DataPrint()
- {
- cout << _year << "/" << _month << "/" << _day << endl;
- }
- private:
- int _year;//年
- int _month;//月
- int _day;//日
- };
- int main()
- {
- Data d1;
- Data d2;
- d1.Dataday(2024,11,12);
- d1.DataPrint();
- d2.Dataday(2024, 10, 11);
- d2.DataPrint();
- return 0;
- }
复制代码 对于上述类,有如许的一个问题:
Date类中有 DataInit 与 DataPrint 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函数时,该函数是怎样知道应该设置d1对象,而不是设置d2对象呢?
C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐蔽的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器主动完成。
this指针:
编译器在编译之后,类的成员函数会默认都会在形参的第一个位置,增加一个当前类类型的指针,叫做this指针,比如在上面的类Data中函数 DataInit和DataPrint的原型就是。
- class Data//日期
- {
- public:
- void Dataday(Data *const this ,int year, int month, int day)
- {
- this->_year = year;
- this->_month = month;
- this->_day = day;
- }
- void DataPrint(Data* const this)
- {
- cout << this->_year << "/" << this->_month << "/" << this->_day << endl;
- }
- //private:
- int _year;//年
- int _month;//月
- int _day;//日
- };
复制代码 但是如果直接怎样写的话程序汇报错的。具体缘故原由是C++规定不能在实参和形参的位置显示的写this指针(编译器会本身处理)但是可以在函数体内里使用。(就像上面的函数体内里的this指针一样)
那在实参的位置不显示写this指针又是怎样的?首先我们要知道想要通过改变形参而改变实参,那就要传一个地址已往,这里我们要传的是类对象的地址,但是我们光从代码的角度去看是没有传地址已往的。
- int main()
- {
- Data d1;
- Data d2;
- d1.Dataday(2024,11,12);
- d1.DataPrint(&d1);//会报错的,这里不加&d1
- d2.Dataday(2024, 10, 11);
- d2.DataPrint(&d2);//会报错的,这里不加&d2
- return 0;
- }
复制代码 这里代码会报错的哟。由于这里函数在调用的时候编译器会主动把本身的类对象的地址传已往。
按照逻辑代码要像如许写才对呀!这里逻辑是没有问题的,但是C++规定:不能在实参和形参的位置显示的写this指针(编译器会本身处理)
this指针的特性
1.this指针的类型是:类类型*const,在成员函数中是不能给this赋值的。
2.只能在“成员函数”的内部使用。
3.this本质上是“成员函数”的形参,当对象调用成员函数的时候将对象的地址作为实参传给this形参。所以对象中不存储this指针
4.this指针是“成员函数”第一个隐含的指针形参,一样平常情况由编译器通过ecx寄存器主动传
递,不需要用户传递
六、C语言和C++实现栈的对比
C语言实现栈之前小编就分享过了,逻辑上时雷同的,但是实现代码是比较不同的:https://editor.csdn.net/md/?articleId=142706511
另有什么问题呢各人可以看看。接下来呢我们用C++来实现栈:
- #include<iostream>
- typedef int SLDataType;
- class Stack
- {
- public :
- void StackInit( SLDataType capacity=4 )//初始化
- {
- SLDataType*tmp = (SLDataType*)malloc(sizeof(SLDataType) * capacity);
- if (tmp == nullptr)
- {
- perror("malloc fail\n");
- exit(1);
- }
- _arr = tmp;
- _capacity = capacity;
- _top = 0;
- }
- void StackPush(const SLDataType& data)//入栈,防止数据被篡改所以这里用到了const修饰
- {
- if (_capacity == _top)
- {
- SLDataType newcapacity = _capacity*2;
- SLDataType* tmp = (SLDataType*)realloc(_arr, newcapacity * sizeof(SLDataType));
- if (tmp == nullptr)
- {
- perror("realloc fail\n");
- exit(1);
- }
- _arr = tmp;
- _capacity = newcapacity;
- }
- _arr[_top++] = data;
- }
- SLDataType StackTop()//取出栈顶元素
- {
- return _arr[_top - 1];
- }
- void StackPop()//出栈
- {
- _top--;
- }
- void StackDestroy()//销毁
- {
- assert(_arr);
- free(_arr);
- _arr = nullptr;
- _capacity = _top = 0;
- }
- bool StackEmpty()//判空
- {
- return _top == 0;
- }
- private://声明
- int* _arr;
- int _capacity;
- int _top;
- };
- int main()
- {
- Stack d1;
- d1.StackInit(1);
- d1.StackPush(1);
- d1.StackPush(2);
- d1.StackPush(3);
- d1.StackPush(4);
- while (!d1.StackEmpty())
- {
- cout << d1.StackTop();
- d1.StackPop();
- }
- return 0;
- }
复制代码 C++实现栈的优点:
1.C++通过类实现栈具有封装性。可以或许防止程序的重要变量被随意修改。
(2)C++通过类实现栈类中默认存在构造函数可以初始化栈对象,也默认存在析构函数可以开释和清理申请的空间(后面小编会分享)。和C语言使用栈相比可以不消在担心在创建栈时是否忘记初始化和删除栈了。
C语言实现栈的缺点:
1.每个函数的第一个参数都是结构体指针(ST*)(以小编之前分享的栈的实现为主)
2.函数中必须要对第一个参数检测,也就是断言一下(assert)由于该参数可能会为NULL
3.函数中都是通过ST*操作栈的
4.调用时必须传结构体变量的地址
5.结构体只是定义和存放数据的结构,操作数据的方法不能放在结构体中,所以操作数据的方式和数据分离的。而去实现相对比较复杂,涉及大量指针操作。容错率低。
七、类的大小计算
先来看看规则:
1.类的大小计算要依照结构体的对齐原则。
https://editor.csdn.net/md?articleId=143825655
2.类的大小与普通数据成员有关,与成员函数和静态成员无关。即普通成员函数,静态成员函数,静态数据成员,静态常量数据成员均对类的大小无影响
- using namespace std;
- class A
- {
- public:
- void Print1()
- {
- cout << "A::Print()" << endl;
- }
- static void Print(); //静态成员函数
- private:
- static const int a =10; //静态常量数据成员,在类外定义时,要加上const关键字
- static int b; //静态数据成员
- };
- int main()
- {
- A b;
- cout << sizeof(b) << endl;
- return 0;
- }
复制代码
代码运行的结果是1,阐明这里没有计算它的大小。那为什么1就是没有计算呢?那就要看第四点了。
3.虚函数对类的大小有影响,是由于虚函数表指针带来的影响,虚继承对类的大小有影响,是由于虚基表指针带来的影响(这一点呢小编不做分享各人知道就行)
4.空类的大小是一个特殊情况,空类的大小为1
那为什么没有成员变量还要给1呢?这里就是纯粹的占位标识对象的存在。如果一个字节都不给,那怎样表示对象存在呢?
总结
今天就到这里吧,有什么问题我们评论区间啦!再见啦!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |