资讯动态

【C++初阶】类和对象(上)

发布时间:2026/8/14 15:44:21 来源:尧图企业网站定制
文章目录一.类的定义1类定义格式注意class/struct定义类的区别2访问限定符注意限定符限定的是是否能在类外被使用无论加不加限定符是什么限定符都不影响类中随意使用。3类域二.实例化1实例化的概念2对象的大小既然学到这不如来回顾一下为什么会有内存对齐规则但如果类中只有成员函数怎么计算对象的大小呢三.this指针小试牛刀一.类的定义1类定义格式class/struct类名{限定符 成员函数/类的方法 成员变量/类的属性};class/structStack{//成员函数/类的方法//初始化voidSTInit(intn4){//……}public:voidSTDestroy(){//……}//成员变量/类的属性private:int*array;size_t size;size_t capacity;};注意①class为定义类的关键字Stack为类的名字{}中为类的主体注意类定义结束时后⾯分号不能省略。类体中内容称为类的成员类中的变量称为类的属性或成员变量类中的函数称为类的⽅法或者成员函数②为了区分成员变量⼀般习惯上成员变量会加⼀个特殊标识如成员变量前⾯或者后⾯加_或者m开头注意C中这个并不是强制的只是⼀些惯例具体看公司的要求classStudent{private:int_age;double_weight;public:voidSet(intage,doubleweight){_ageage;_weightweight;}};③C中struct也可以定义类C兼容C中struct的⽤法同时struct升级成了类明显的变化是struct中可以定义函数在C语言中结构体里不能定义函数⼀般情况下我们还是推荐⽤class定义类。class/struct定义类的区别class定义的类中没有限定符修饰的成员默认为私有但在struct定义的类中没有被限定符修饰的成员默认为公有。④定义在类里面的成员函数默认为inline函数。但是如果是在类外定义在类里面声明不算是内联函数⑤在类中规范写法是先写成员函数再写成员变量。2访问限定符C⼀种实现封装的⽅式⽤类将对象的属性与⽅法结合在⼀块让对象更加完善通过访问权限选择性的将其接⼝提供给外部的⽤⼾使⽤。public公有修饰的成员在类外可以直接被访问protected和private私有修饰的成员在类外不能直接被访问protected和private是⼀样的以后继承章节才能体现出他们的区别。注意限定符限定的是是否能在类外被使用无论加不加限定符是什么限定符都不影响类中随意使用。访问权限作⽤域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为⽌如果后⾯没有访问限定符作⽤域就到}即类结束。例如下面的代码中array、size和capacity都是private限定的。classStack{private:int*array;size_t size;size_t capacity;}class定义成员没有被访问限定符修饰时默认为privatestruct默认为public。⼀般成员变量都会被限制为private/protected需要给别⼈使⽤的成员函数会放为public。3类域作用域全局域、局部域、命名空间域和类域。类定义了⼀个新的作⽤域类的所有成员都在类的作⽤域中当在类内声明函数。类外定义成员函数时就需要使⽤类名::作⽤域操作符指明成员属于哪个类域这样才能指明该函数是类的成员函数也能正常使用类内的私有(private)成员变量。类域影响的是编译的查找规则。下⾯程序中Init如果不指定类域Stack那么编译器就把Init当成一个普通全局函数。那么编译时只会在全局作用域里找就找不到array等成员的声明/定义在哪⾥那么就会报错。只有指定类域Stack才知道Init是类的成员函数就会到类域中去查找array等成员。#includeiostreamusingnamespacestd;classStack{public:// 成员函数的声明voidInit(intn4);private:// 成员变量int*array;size_t capacity;size_t top;};// 声明和定义分离需要指定类域voidStack::Init(intn){array(int*)malloc(sizeof(int)*n);if(nullptrarray){perror(malloc申请空间失败);return;}capacityn;top0;}intmain(){Stack st;st.Init();return0;}类域和命名空间域并不影响生命周期它俩都是放在全局生命周期里的。二.实例化1实例化的概念⽤类类型在物理内存中创建对象的过程称为类实例化出对象。类是对对象进⾏的⼀种抽象描述是⼀个模型⼀样的东西限定了类有哪些成员变量。这些成员变量只是声明没有分配空间⽤类实例化出对象时才会分配空间。这也说明了声明和定义的区别就是前者没有分配内存空间而后者分配了内存空间⼀个类可以实例化出多个对象实例化出的对象占⽤实际的物理空间存储类成员变量。打个比方类就像房屋的设计图纸而实例化出的一个个对象就是一个个具体的占据了空间的房子房子可以有多栋。2对象的大小分析⼀下类对象中哪些成员呢类实例化出的每个对象都有独⽴的数据空间所以对象中肯定包含成员变量。那么成员函数是否包含呢⾸先函数被编译后是⼀段指令对象中没办法存储这些指令被存储在⼀个单独的区域(代码段)那么对象中⾮要存储的话只能是成员函数的指针可以认为是第一句指令的地址。再分析⼀下对象中是否有存储指针的必要呢Date实例化d1和d2两个对象d1和d2都有各⾃独⽴的成员变量_year/_month/_day存储各⾃的数据但是d1和d2的成员函数Init/Print指针却是⼀样的存储在对象中就浪费了。如果⽤Date实例化100个对象那么成员函数指针就重复存储100次太浪费了。故而不需要包含成员函数。C中实例化对象也要符合内存对齐的规则。贴一下内存对齐的规则的图片和结构体存储的内存规则对齐完全相同※面试常考既然学到这不如来回顾一下为什么会有内存对齐规则最根本的原因硬件CPU读取限制。现代CPU无法任意读取内存单字节只能按固定块对齐粒度常见4/8字节 读取内存假设CPU一次读取8字节64位处理器对齐数据变量起始地址是8的倍数CPU1次内存访问就能完整读出数据未对齐数据变量跨两个8字节内存块CPU必须读两次内存再拼接数据性能大幅下降。但如果类中只有成员函数怎么计算对象的大小呢统一规定类中只有成员函数则实例化对象的大小为1。这里之所以给1字节纯粹是因为占位标识对象存在。三.this指针当类实例化出多个对象之后每次传参调用函数函数体中又没有关于不同对象的区分那函数怎么知道要访问哪个对象呢譬如下列代码实例化出d1、d2当d1调用初始化函数时函数是怎么知道要访问d1对象的呢#includeiostream//工具集合usingnamespacestd;//准许使用书classDate{public:voidInit(intyear,intmonth,intday){_yearyear;_monthmonth;_dayday;}voidPrint(){cout_year _month _day endl;}private:int_year;int_month;int_day;};intmain(){Date d1;Date d2;d1.Init(2026,8,13);d1.Print();d2.Init(2007,7,31);d2.Print();return0;}C给了⼀个隐含的this指针来解决这⾥的问题。编译器编译后类的成员函数默认都会在形参第⼀个位置增加⼀个当前类类型的指针叫做this指针。⽐如Date类的Init的真实原型为 void Init(Date* const this, int year,int month, int day)类的成员函数中访问成员变量本质都是通过this指针访问的如Init函数中给_year赋值 this-_year year。this指针存在内存的栈中。所以代码也可以写成classDate{public://void Init(Date* const this,int year,int month,int day)voidInit(intyear,intmonth,intday){this-_yearyear;this-_monthmonth;this-_dayday;}voidPrint(){cout_year _month _day endl;}private:int_year;int_month;int_day;};C规定不能在实参和形参的位置显⽰的写this指针(编译时编译器会处理)但是可以在函数体内显⽰使⽤this指针。小试牛刀1.下⾯程序编译运⾏结果是A、编译报错 B、运⾏崩溃 C、正常运⾏#includeiostreamusingnamespacestd;classA{public:voidPrint(){coutA::Print()endl;}private:int_a;};intmain(){A*pnullptr;p-Print();return0;}答案是C正常运行。可能有人一看见p-Print就认为是空指针解引用程序会崩溃但其实这里并没有空指针解引用。主函数中定义了一个类型类的指针并初始化该指针为空nullptr再通过指针调用类中的成员函数。在前面我们说C规定类的成员函数的第一个参数默认为类类型的指针也就是说这里定义的指针p传给了指针this此时this指针为空。在调用Print函数时函数内部并没有解引用this空指针仅仅只是进行了打印动作故而并不会出现空指针解引用程序崩溃的情况。2.下⾯程序编译运⾏结果是A、编译报错 B、运⾏崩溃 C、正常运⾏#includeiostreamusingnamespacestd;classA{public:voidPrint(){coutA::Print()endl;cout_aendl;}private:int_a;};intmain(){A*pnullptr;p-Print();return0;}有了第一题的分析这题很明显选B程序崩溃。——end——

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价