C++ 篇 类和对象(1)万能工具怎么用?
🎬 胖咕噜的稞达鸭:个人主页
一.类的定义
- 类定义格式
- class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。类体中内容称为类的成员:类中的变量称为类的属性或成员变量;类中的函数称为类的方法或者成员函数。
#include<iostream>
#include<assert.h>
using namespace std;//class 定义类的关键字,stack 是类的名字,{ }中为类的主体
class Stack
{
public://成员函数void Init(int n = 4){array = (int*)malloc(sizeof(int) * n);if (nullptr == array){perror("malloc申请空间失败");return;}capacity = n;top = 0;}void Push(int x){//扩容array[top++] = x;}int Top(){assert(top > 0);return array[top - 1];}void Destroy(){free(array);array = nullptr;top = capacity = 0;}private://成员变量 private表示其一下的内容不可修改int* array;//栈中的数组size_t capacity;size_t top;
};//这个分号不可省略int main()//没有返回值也不可写void!!!
{Stack st;//类型可以访问 //st是栈对象st.Init();//通过对象成员函数()调用,编译器会隐式传递this指针(指回当前对象),让成员函数能访问对象的私有成员st.Push(1);st.Push(2);//栈顶为2,栈底为1cout << st.Top() << endl;// << 输出 通过cout输出到控制台st.Destroy();return 0;
}
- 为了区分成员变量,一般习惯上成员变量会加一个特殊标识,如成员变量前面或者后面加_或者_m开头,注意C++中这个并不是强制的。
class Date
{
public://成员函数void Init(int year, int month, int day){_year = year;_month = month;_day = day;}
private://成员变量//加一个特殊标记,_或者_m开头int _year;int _month;int _day;
};int main()
{Date d;d.Init(2025, 9, 18);return 0;
}
- C++中struct也可以定义类,C++兼容C中struct的用法,同时struct升级成了类,明显的变化是struct中可以定义函数,一般情况下我们还是推荐用class定义类。
typedef int STDataType;
typedef struct ListNode
{int val;struct ListNode* next;
}LTnode;
#include<iostream>
using namespace std;struct ListNodeCPP
{//成员函数void Init(int x){next = nullptr;val = x;}//成员变量ListNodeCPP* next;int val;
};int main(){return 0;}
定义在类面前的成员函数默认为inline。
- 访问限定符
三种 | public |
---|---|
访问 | private |
限定符 | protected |
public修饰的成员在类外可以直接被访问;protected和private修饰的成员在类外不能被直接访问,private和protected是一样的。 | |
访问权限作用域从该访问限定符开始的位置到下一个访问限定符的出现时为止,如果后面没有访问 限定符,作用域到}就结束。 | |
class定义成员没有被访问限定符修饰的时候默认为private,struct默认为public。 |
- 类域
#include<iostream>
using namespace std;
class Stack
{
public:// 成员函数void Init(int n = 4);
private:// 成员变量int* array;size_t capacity;size_t top;
};
// 声明和定义分离,需要指定类域
void Stack::Init(int n)//说明Init不是一个全局函数,是类的成员函数
{array = (int*)malloc(sizeof(int) * n);if (nullptr == array){perror("malloc申请空间失败");return;}capacity = n;top = 0;
}
int main()
{Stack st;st.Init();return 0;
}
面试题插入:说出声明和定义的区别:
声明就是告诉变量的类型;定义要开空间。
二.实例化
- 什么是实例化
类是对象进行一种抽象描述,是一个模型一样的东西,限定了类有哪些成员变量,这些成员变量只是声明,没有分配空间,用类实例化出对象时,才会分配空间。类和实例化对象的关系相当于一张图纸和许多建筑的关系。 - 对象大小
Date实例化d1和d2两个对象,d1和d2都有各自独立的成员变量_year/_month/_day存储各自的数据,但是d1和d2的成员函数Init/Print指针却是一样的,存储在对象中就浪费了。
调用函数被编译成汇编指令[call地址],其实编译器在编译链接时,就要找到函数的地址,不是在运行时找,只有动态多态是在运行时找,就需要存储函数地址。 - 内存对齐规则
- [1 ] 第一个成员在与结构体偏移量为0的地址处,
- [2 ] 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
- [ 3] 注意:对齐数=编译器默认的一个对齐数与该成员大小的较小值。
- [ 4] VS中默认的对齐数为8
- [ 5] 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
- [ 6] 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
下面给三个例子算一下实例化对象分别有多大?
#include<iostream>
using namespace std;// 计算一下A / B / C实例化的对象是多大?
class A
{
public:void Print(){ cout << _ch << endl;}
private:char _ch; int _i;
};
class B
{
public:void Print() {//...}
};
class C
{};
int main()
{A a;B b;C c;cout << sizeof(a) << endl;cout << sizeof(b) << endl;cout << sizeof(c) << endl; //1B和C大小一致,虽然没有成员变量的类,但是它存在就要开空间,给一个字节,纯粹就是为了占位标记return 0;
}
三.this指针
C++规定不能在实参和形参的位置显示的写this指针(编译时编译器会处理),但是可以在函数体内显示使用this指针。
#include<iostream>
using namespace std;
class Date
{
public:// void Init(Date* const this, int year, int month, int day)//所有成员函数都会增加隐含的this指针void Init(int year, int month, int day){this->_year = year;this->_month = month;this->_day = day;}// void Print(Date* const this)void Print(){cout << this->_year << "/" << this->_month << "/" << _day << endl;}private:// 这里只是声明,没有开空间int _year;int _month;int _day;
};int main()
{// Date类实例化出对象d1和d2Date d1;Date d2;// d1.Init(&d1, 2024, 3, 31);d1.Init(2024, 3, 31);// d1.Print(&d1);d1.Print();// d2.Init(&d2, 2024, 7, 5);d2.Init(2024, 7, 5);// d2.Print(&d2);d2.Print();//不可以写成d2.Print(d2);//但是可以在函数体内写return 0;
}
下面以两个选择题结束今天的学习
1.这段代码会编译运行结果是()
#include<iostream>
using namespace std;class A
{
public:void Print(){//cout << this << endl;cout << "A::Print()" << endl;}
private:int _a;
};int main()
{A* p = nullptr;// mov ecx pp->Print(); // call 地址//p->_a = 1;return 0;
}
2.下面程序编译运行的结果是编译报错为什么?
#include<iostream>
using namespace std;class A
{
public:void Print(){cout << "A::Print()" << endl;cout << _a << endl;//_a需要存在对象A里面,这里访问_a是通过this来访问的(相当于解引用)}
private:int _a;
};int main()
{A* p = nullptr;// mov ecx pp->Print(); // call 地址//调用Print函数传参,把P传给ecx,call位置是地址,地址不存对象P里面,不存在解引用//不会从对象里面找,地址是在编译的时候就确定的return 0;
}