ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

C++类和对象(上):从定义格式到this指针的深度解析

C++类和对象(上):从定义格式到this指针的深度解析 一.类的定义1.类定义格式前言类和对象是最开始祖师爷最想加入的东西最初C叫做“带类的C”在不断的加入新语法后衍化成今天的C。• class为定义类的关键字后加一个类的名字如Stack后加一个{}内部放入类的主体注意类定义的{}结束后面的;不能省略。类主体中的内容称为类的成员类中的变量称为类的成员变量或属性类中的函数称为类的成员函数或方法。变化可以在内部放入函数了这样就可以变量和方法集中在一起。class Stack { int* _arr; int _size; int _capacity; };• 为了区分成员变量和外部的普通变量一般习惯上会给成员变量加一个特殊标识这不是C中强制规定的是为了写代码时方便区分和增加可读性而产生的惯例具体看公司要求。class Date { int _year; int _month; int _day; };• C中的struct也可以定义类C在兼容C的同时把struct升级成了类。最明显的变化是struct中可以定义函数了但是一般情况下我们还是推荐使用class去定义类。C和C中的struct的区别如下//test.c typedef struct ListNode { struct ListNode* next; int val; }LTNode; //test.cpp struct ListNodecpp { ListNodecpp* next; //可以在内部使用名字去定义变量了不用加struct int val; }; int main() { //.c使用了typedef去重命名 LTNode* N1; //.cpp不使用typedef去重命名也不用去加上前面的struct ListNodecpp* N2; return 0; }• 直接定义在类里面的成员函数不做声明和定义分离默认为inline正好inline函数也不支持声明和定义分离。学到这我们可以简单的写一个类去运用一下新接收的知识。class Date { void Init(int year,int month, int day) { _year year; _month month; _day day; //假设成员变量不加特殊标识就会出现 //day day; //因为查找变量是现在局部找它只会把形参day赋值给形参day出现自己给自己赋值的情况 //要不然就这样写也行 //Date::day day; } int _year; int _month; int _day; };2.访问限定符• public修饰的成员在类外可以直接被访问protected和private修饰的成员在类外不能直接访问(类内还是可以的)目前protected和private是一样的在后面的继承章节才能体现出二者的区别。• 这是C一种实现封装(约束)的方式用类将对象的属性和方法结合在一起让对象整体更加完善再通过访问限定符选择性的将接口提供给外部的用户使用。• 访问权限作用域是从该访问限定符出现的位置开始直到下一个访问限定符出现为止如果后面没有访问限定符了那作用域就到 } 位置即类结束。class Date { public: void Init(int year, int month, int day) { _year year; _month month; _day day; } private: int _year; int _month; int _day; };• C中class和struct的基本区别class中定义成员没有被访问限定符修饰时默认为privatestruct则默认为public要兼容C语言• 一般成员变量都会被限制为private/protected需要给别人使用的成员函数才会放为public。3.类域• 在前文namespace的地方我们也讲解到类定义了一个新的作用域类的所有成员函数都在类的作用域中在类体外定义成员时在类体内要有其声明就是声明和定义分离时需要使用 :: 作用域限定符指明其属于哪个类域。具体如下class Date { public: void Print(); void Init(int year, int month, int day) { _year year; _month month; _day day; } private: int _year; int _month; int _day; }; void Date::Print() { cout _year / _month / _day endl; }• 和命名空间域一样类域影响的是编译的查找规则。二.实例化1.实例化概念• 用类的类型去在物理内存中创建对象的过程称为类实例化出对象。(说的绕演示一下就好了)//这是一个类 class Student { char* name; char* sex; int age; }; int main() { //用这个类的类型去定义出一个对象这就是实例化了 Student lgf; return 0; }• 类是对象的一个抽象描述是一个类似模具一样的东西限定了对象中有那些成员变量这些成员变量只是声明没有开辟空间(在类之中)只有当用类去实例化出对象时才会去分配空间。• 一个类可以实例化出多个对象实例化出的对象占用实际的物理空间存储类成员变量。2.对象大小• 简单来说一个对象的大小就考虑两点即可①只存成员变量并且有内存对齐。②普通函数不存只有动态多态是在对象中存储的。• 在这里可能有同学有疑问为什么我们实例化出来的对象只存储成员变量呢函数不用存储吗这是因为由一个相同的类去定义出多个对象后多个对象内部存储的数据是一定要分开存储在不同空间的但是它们使用的函数都是相同的去最开始的类定义里找即可不用去存储在每个对象中还浪费空间。class Date { public: void Print(); void Init(int year, int month, int day) { _year year; _month month; _day day; } private: int _year; int _month; int _day; }; void Date::Print() { cout _year / _month / _day endl; } int main() { Date d1; d1.Init(2026, 9, 30); d1.Print(); Date d2; d2.Init(2021, 4, 7); d2.Print(); cout sizeof(d1) endl; return 0; }内存对齐规则(在C中学struct就学过了这里简单复习一下即可)• 第一个成员在与结构体偏移量为0的地址处。• 其他成员变量要对齐到某个数字对齐数的整数倍的地址处。• 注意对齐数 编译器默认的一个对齐数 与 该成员大小的较小值。• VS中默认的对齐数为8• 结构体总大小为最大对齐数所有变量类型最大者与默认对齐参数取最小的整数倍。• 如果嵌套了结构体的情况嵌套的结构体对齐到自己的最大对齐数的整数倍处结构体的整体大 小就是所有最大对齐数含嵌套结构体的对齐数的整数倍。让我们看以下两个类去猜一下它们的大小class A { }; class B { void print() { //。。。 } }; int main() { A a; B b; cout sizeof(a) endl; cout sizeof(b) endl; }上面的程序运行后我们看到没有成员变量的A和B类对象的大小都是1为什么没有成员变量还要给1个字节呢因为如果一个字节都不给怎么表示对象存在过呢所以这里给1字节纯粹是为了占位标识对象存在。三.this指针• Date类中有Print和Init两个成员函数既然我们上文提到对象中不存储成员函数那么当d1调用print和Init函数是怎么知道应该去访问d1对象还是d2对象呢这里就涉及到了C给了一个隐含的this指针去解决该问题。• 编译器在编译后类成员函数默认都会在形参的第一个位置增加一个当前类类型的指针叫做this指针。比如Date的Init的真实原型为void Init(Date* const this, int year, int month, int day)有趣的是this指针被const修饰了且修饰的是this本身而不是*this这则表明this的指向不可被改变但是this* 的内容可以。• 类成员中访问成员变量本质上都是通过this指针访问的如Init函数中给_year赋值编译器处理后为(注意这是概念模型不是真实可编译代码)void Init(Date* const this, int year, int month, int day) { this-_year year; this-_month month; this-_day day; }而在调用时编译器处理后为Date d; d.Init(2024, 9, 30); // 编译器转换成 Init(d, 2024, 9, 30);//在编译过后Init变成了普通函数调用把d.拆成了d作为第一个实参• C规定不能在实参和形参的位置显示的写this指针编译时编译器会自己处理但是在函数体的内部可以显示的使用this指针。class Date { public: void Print() { cout this endl; } void Init(int year, int month, int day) { _year year; _month month; _day day; } private: int _year; int _month; int _day; }; int main() { Date d; d.Print(); return 0; }在这里的print函数就能打印出this的地址了。完。
返回列表