【C++】类和对象1
类和对象11、类的定义C中可以用struct定义一个类但更多的是使用class定义类举例C语言中的结构体// C 语言仅能定义变量不能定义函数structStudent{charname[20];intage;// 错误C 不允许结构体里写函数// void show() { ... }};C中的类// C 类变量 函数 访问控制class Student{private:// 成员变量属性std::string name;intage;public:// 成员函数方法voidsetInfo(std::string n,inta){namen;agea;}voidshowInfo(){std::coutname agestd::endl;}};C中struct也可以定义类C兼容C中struct的用法同时struct升级成了类明显的变化是struct中可以定义函数一般情况下我们还是推荐用class定义类。类的两种定义方法声明和定义全部放在类体中//定义一个栈的结构体class Stack{public://初始化栈voidInit(intn4){array(int*)malloc(sizeof(int)*n);if(nullptrarray){perror(malloc申请空间失败);return;}capacityn;top0;}int*array;size_tcapacity;size_ttop;};注意成员函数如果在类中定义编译器可能会将其当成内联函数处理最终是否内联由编译器决定类的声明放在.h文件中成员函数的定义放在.cpp文件中Stack.h文件 class Stack{public://成员函数//定义在类⾯的成员函数默认为inlinevoidInit(intn4);//成员变量int*array;size_tcapacity;size_ttop;};Stack.cpp文件#includeF.hvoidStack::Init(intn)//这里不能写int n4;因为默认参数只能写在函数声明中{array(int*)malloc(sizeof(int)*n);if(nullptrarray){perror(malloc申请空间失败);return;}capacityn;top0;}注意成员函数名前要加类名::告诉编译器这个函数属于哪个类域2、类的访问限定符public公有private私有protected私有 说明public修饰的成员在类外面可以直接被访问protect和private修饰的成员在类外不能直接被访问protected和private是一样的class定义成员没有被访问限定符修饰时默认为privatestruct默认为public一般成员变量都会被限制为private/protected需要给别人使用的成员函数会放为public3、类域类定义了一个新的作用域类的所有成员都在类的作用域中#includeiostreamusing namespace std;class Stack{public:// 成员函数voidInit(intn4);private:// 成员变量int*array;size_tcapacity;size_ttop;};// 声明和定义分离需要指定类域voidStack::Init(intn){array(int*)malloc(sizeof(int)*n);if(nullptrarray){perror(malloc申请空间失败);return;}capacityn;top0;}intmain(){Stack st;st.Init();return0;}如果Init不指定类域Stack那么编辑器就把Init当成全局函数那么编译时找不到array等成员的声明/定义在哪里就会报错。指定类域Stack就会去对应的类域里面搜索当类域里面也没有时最后回到全局搜索如果全局没有编译就会报错4、类的实例化实例化概念用类类型在物理内存中创建对象的过程称为类的实例化类是对象进行一种抽象描述是一个模型一样的东西限定了类有哪些成员只是声明没有分配空间。类和对象的关系可以看成使用建筑设计图造出房子类是设计图不能存数据实例化出的对象才能分配物理内存存储数据class Date{public:voidInit(intyear,intmonth,intday){_yearyear;_monthmonth;_dayday;}voidPrint(){cout_year-_month-_dayendl;}private:int_year;//声明 没有空间//只能由实例化的对象分配为例内存存储数据int_month;int_day;};intmain(){Date d1;Date d2;//实例化对象出d1和d2//Init函数指针是一样的 不需要独立空间d1.Init(2022,1,1);d2.Init(2022,1,2);d1.Print();d2.Print();return0;}错误示范Date._year2026;相当于在图纸里住人5、类的对象类对象的大小#includeiostreamusing namespace std;// 计算⼀下A/B/C实例化的对象是多⼤//类中既有成员变量又有成员函数class A{public:voidPrint(){cout_chendl;}private:char_ch;int_i;};//类中只有成员函数class B{public:voidPrint(){//...}};//类中什么也没有空类class C{};intmain(){A a;B b;C c;coutA的大小sizeof(a)endl;coutB的大小sizeof(b)endl;coutC的大小sizeof(c)endl;return0;}运行结果A的大小8B的大小1C的大小1类的大小实际就是该类中“成员变量”之和要遵守内存对齐规则没有成员变量的类编译器通常会给一个字节来占位标识对象的存在拿Date类举例Date实例化d1和d2两个对象d1和d2都有各自独立的成员变量_year/_month/_day存储各自的数据但是d1和d2的成员函数Init指针却是一样的如果存储在对象中每次实例化时成员函数就重复存储一次浪费空间所以成员函数要放在公共代码区以建房子为例类是图纸实例化的对象是按照图纸造出来的房子成员变量相当于房子中的房间而成员函数相当于公共场所不需要建在每一个房子里建在公共场所大家都可以使用注意sizeof(类)和sizeof(对象)计算出来的结果一样内存对齐规则第一个成员在与结构体偏移量为0的地址处。其他成员变量要对齐到某个数字对齐数的整数倍的地址处。注意对齐数 编译器默认的⼀个对齐数 与 该成员大小的较小值。结构体总大小为最大对齐数所有变量类型最大者与默认对齐参数取最小的整数倍。如果嵌套了结构体的情况嵌套的结构体对齐到自己的最大对齐数的整数倍处结构体的整体大小就是所有最大对齐数含嵌套结构体的对齐数的整数倍。class A{public:voidPrint(){}private:char_ch;int_i;};编译器一次读取4个字节在内存没对齐的情况下读取时非常麻烦要把第一个读取的字节去掉再把第二次读取的和第一个字节拼接而在内存对齐的情况下只需读取2次即可6、this指针//定义一个日期类class Date{public:voidInit(intyear,intmonth,intday){_yearyear;this-_monthmonth;this-_dayday;}voidPrint(){cout_year/_month/_dayendl;}private:// 这⾥只是声明没有开空间int_year;int_month;int_day;};intmain(){// Date类实例化出对象d1和d2Date d1;Date d2;// d1.Init(d1, 2024, 3, 31);d1.Init(2024,3,31);//d1初始化d1.Print();//打印d2.Init(2024,7,5);//d2初始化d2.Print();//打印return0;}输出结果2024/3/312024/7/5那当d1调用Init和Print函数时该函数是如何知道应该访问的是d1对象还是d2对象呢这里C给了一个隐含的this指针解决这里的问题编译器编译后类的成员函数会在形参的第一个位置增加一个当前类类型的指针叫做this函数题中的有关成员变量的操作都需要this指针访问比如Print函数voidPrint(){cout_year/_month/_dayendl;}真实原型为voidPrint(Date*constthis){coutthis-_year/this-_month/this-_dayendl;}d1和d2是同一个函数调用时传递的参数不同所以调用谁打印的就是谁的结果。C规定不能在实参和形参的位置显示的写this指针(编译时编译器会处理)但是可以在函数体内显示使用this指针。注意this存储在栈中this指针和普通的函数形参一样在函数调用的时候入栈存储在函数的调用栈帧里在成员函数中不能修改this指针的指向this指针是成员函数隐藏的指针形参不需要写this1.下面程序编译运行结果是A、编译报错 B、运行崩溃 C、正常运行#includeiostreamusing namespace std;class A{public:voidPrint(){coutA::Print()endl;}private:int_a;};intmain(){A*pnullptr;p-Print();return0;}定义一个A类型的指针p并置为空调用p的成员函数Print不发生解引用Print函数的地址不在对象中所以不会报错正确答案为C正常运行因为没有访问任何类中的其他成员根本没有使用this指针2.下面程序编译运行结果是A、编译报错 B、运行崩溃 C、正常运行#includeiostreamusing namespace std;class A{public:voidPrint(){coutA::Print()endl;cout_aendl;}private:int_a;};intmain(){A*pnullptr;p-Print();return0;}定义一个A类型的指针p并置为空调用p的成员函数Print在Print函数中使用了其他成员_a相当于this→_athis是空指针所以这就成为了解引用空指针运行崩溃如果文章中有错误或不足欢迎大家指正交流。