【C++】--- 类和对象(上)
类和对象上1. 类的定义1.1 类定义格式1.2 访问限定符1.3 类域2. 实例化2.1 实例化概念2.2 对象大小3. this指针4. C和C中Stack的对比1. 类的定义1.1 类定义格式class是为定义类的关键字定义类的语法为class 类名 { };,{}中为类的主体类体中内容称为类的成员类中的变量称为类的属性或者成员变量类中的函数称为类的方法或者成员函数。下面给出一个学生类的简单定义classStudent{int_age;int_height;voidprint(){cout这是一个学生类的方法endl;}voidInit(intage,intheight){_ageage;_heightheight;}};为了区分形参和实参我们一般在成员变量的前面或者后面加上_或者m。我们可以在创建学生类的一个实例intmain(){Student s1;return0;}值得一提的是在C语言中创建一个结构体实例如果不使用typedef对结构体重命名的话定义一个学生的结构体需要加上struct关键字但是在C中这不是必要的类名就是类型当然结构体名也是类型。structListNode{structListNode*next;intval;};这是C语言中单链表结点定义方式C兼容C的语法。structListNode{ListNode*next;intval;};C就可以这样定义啦定义在类中的成员函数默认为inline现在知道这一点就可以了后买我们才会较多的使用。1.2 访问限定符可以看到我们无法访问任何一个成员变量和成员方法它们都是private私有的成员这就要引出访问限定符了。C提供了三种访问限定符public修饰的成员在类外是可以访问的proctect和private修改时的成员在类外是无法被访问到的这里protect和private是一样的后面继承章节才能体现出他们的区别。class定义成员的时候默认的访问限定符为private所以实例无法访问到类的成员。下面我们做出修改classStudent{private:int_age;int_height;public:voidInit(intage,intheight){_ageage;_heightheight;}voidprint(){cout这是一个学生类的方法endl;}};访问限定符作用域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为⽌如果后面没有访问限定符作用域就到}即类结束所以上述代码就是给Init()和print()函数打开了类外的访问权限这两个函数可以被访问到。⼀般成员变量都会被限制为private/protected需要给别人使用的成员函数会放为public。这就要提到类和对象的第一大核心思想封装类相比于结构体的优势就是实现了方法和变量的集成以及数据的私有化在C语言中定义一个Stack的话变量和方法是分别定义的并且变量是裸露的可以随意访问到的这就涉及到安全性的问题。C中类以及访问限定符解决了这个问题我们把数据私有只提供方法给外部的用户使用。C中的struct也可以定义类C兼容C中struct的用法同时把struct升级为了类明显的变化是struct中可以定义函数但是一般情况下我们推荐使用class来定义类使用struct定义类成员的时候默认的访问限定符是public。1.3 类域在类体外定义成员时需要使⽤ :: 作用域操作符指明成员属于哪个类域。例如规范定义一个栈的时候在Stack.h文件下的代码如下#includeiostream#includeassert.husingnamespacestd;classStack{public:voidInit(intn4);private:int*array;inttop;intcapacity;};Stack.c的文件的代码如下#includeStack.hvoidStack::Init(intn){array(int*)malloc(sizeof(int)*n);if(nullptrarray){perror(malloc fail);return;}capacityn;top0;}为什么需要指定作用域呢类域影响的是编译的查找规则下⾯程序中Init如果不指定类域Stack那么编译器就把Init当成全局函数那么编译时找不到array等成员的声明/定义在哪⾥就会报错。指定类域Stack就是知道Init是成员函数当前域找不到的array等成员就会到类域中去查找。2. 实例化2.1 实例化概念用类类型在物理内存中创建对象的过程称为实例化出对象。类是对象进行⼀种抽象描述是⼀个模型⼀样的东西限定了类有哪些成员变量这些成员变量只是声明没有分配空间⽤类实例化出对象时才会分配空间。我们后面经常用到的是一个日期类下面给出他的代码。#includeiostreamusingnamespacestd;classDate{public:voidInit(intyear,intmonth,intday){_yearyear;_monthmonth;_dayday;}voidPrint(){cout_year/_month/_dayendl;}private:int_year;int_month;int_day;};这里是日期类的定义成员变量仅仅是声明没有开空间。⼀个类可以实例化出多个对象。intmain(){Date d1;d1.Init(2020,1,1);d1.Print();Date d2;d2.Init(1,1,1);d2.Print();return0;}类实例化出对象的时候成员变量随着实例化被定义才会开空间。这个时候成员变量还没有初始化不能根据变量是否被初始化来判断变量是否被定义当变量被开空间意味者变量被定义了。2.2 对象大小我们前面说到类被实例化成员变量就定义成功了系统就已经给成员变量开了空间那么成员函数是否被包含呢intmain(){Date d1;coutsizeof(d1)endl;return0;}类对象大小的确定也要考虑内存对齐内存对齐规则和结构体的内存对齐一致。d1的大小为12这说明d1对象中只存储了三个整形的成员变量成员函数并没有被存储。其实类的成员函数被单独存储在代码段中其实成员函数没有必要存储在对象中因为实例化对象的数据是私有的每个对象的数据可能不同需要单独存储但是类的成员函数都是相同的存储在对象中就太浪费了。classB{public:voidPrint(){//...}};classC{};intmain(){B b;C c;;coutsizeof(b)endl;coutsizeof(c)endl;return0;}我们看到没有成员变量的B和C类对象的大小是1为什么没有成员函数还要给1个字节呢这里给1字节存粹是为了占位表示对象存在不存储有效数据。3. this指针intmain(){Date d1;Date d2;d1.Init(2024,3,31);d1.Print();d2.Init(2024,3,4);d2.Print();}我们对Date类实例化出了两个对象并分别调用它们的Init和 Print方法。函数体中没有关于不同对象的区分那当d1调用Print函数的时候函数如何知道该访问d1对象还是d2对象呢这里其实是C给了一个隐含的this指针解决了这个问题。//void Init(Date* const this,int year, int month, int day)voidInit(intyear,intmonth,intday){this-_yearyear;//一般不会这样来写这样写一般都是菜鸟选手this-_monthmonth;this-_dayday;}//void print(Date* const this)voidprint(){coutthis-_year/this-_month/this-_dayendl;cout_year/_month/_dayendl;}//d1.Init(d1,2929, 1, 2);d1.Init(2929,1,2);其实类的成员函数在编译的时候都会在形参的第一个位置添加一个当前类型的this指针对象在调用成员函数的时候在实参的第一个位置也把自己的地址传上去了这样就做到了不同的对象调用相同的成员函数由不同的效果了。这里提出一点C规定不能在实参和形参的位置显式的给出this指针但是在成员函数体中可以给出。4. C和C中Stack的对比C语言实现栈的代码#includestdio.h#includestdlib.h#includestdbool.h#includeassert.htypedefintSTDataType;typedefstructStack{STDataType*a;inttop;intcapacity;}ST;voidSTInit(ST*psintn){assert(ps);ps-a(STDataType*)malloc(sizeof(STDataType)*n);if(aNULL){perror(malloc申请空间失败);return;}ps-top0;ps-capacity0;}voidSTDestroy(ST*ps){assert(ps);free(ps-a);ps-aNULL;ps-topps-capacity0;}//...C实现栈的代码#includeiostreamusingnamespacestd;typedefintSTDataType;classStack{public:// 成员函数voidInit(intn4){_a(STDataType*)malloc(sizeof(STDataType)*n);if(nullptr_a){perror(malloc申请空间失败);return;}_capacityn;_top0;}voidDestroy(){free(_a);_anullptr;_top_capacity0;}//成员方法privateint*_a;int_capacity;int_top;};C中数据和函数都放到了类里面通过访问限定符进行了限制不能再随意通过对象直接修改数据这是C封装的⼀种体现这个是最重要的变化。C中有些相对方便的语法比如Init给的缺省参数会方便很多成员函数每次不需要传对象地址因为this指针隐式传递了使用类型不再需要typedef关键字。这里使用C实现的栈跟C语言没有本质的差别但是后面学到STL用适配器实现Stack的时候就会体现C的魅力。

相关新闻

最新新闻

日新闻

周新闻

月新闻