

声明:以下知识相关资料来自比特官网和小编手搓~
类和对象(上)++1、类的定义++
1.1 类定义格式
1.2 访问限定符
1.3 类域
++2、实例化++
2.1 实例化概念
2.2 对象大小
++3、this指针++
++4.C++和C语言实现Stack对比++
1、类的定义:
在C++中,本贾尼祖师爷对C语言进行了创新再改造,对于自定义类型的创建设计出C++新有的 类 来进行相关操作。
1.1、类定义的格式:
class是定义类的关键字,后跟你要创建自定义类型的具体名称,这个具体名在往后就可以直接当类型使用,接着 {} 内是类具体要定义的主体,里面可以定义 变量 以及 函数 ,最后不要忘记 {} 后面要加 ; 。
在类主体里面定义的变量叫做成员变量(或是成员属性);在类主体定义的函数叫做成员函数(或是成员方法)。
为了区分变量和让形参有更好的可读性,一般会习惯让变量加上特殊标识符,比如现有变量 a ,加上特殊标识符有 a/a/m_a,这些要求就看公司具体习惯了,我们后面的代码样例选用第一个作为变量的添加特殊标识符的例子。
cpp
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
// 为了区分成员变量,⼀般习惯上成员变量
// 会加⼀个特殊标识,如_ 或者 m开头
int _year; // year_ m_year
int _month;
int _day;
};
C++中也是可以用struct定义类的,在此C++对C语言中的struct进行了升级,定义的方法和class关键字一样,最大的区别就是C++的struct升级后主体内可以创建函数。struct后面跟的具体名,可以直接当定义的类的类型用了,不用像C语言那样麻烦的再加struct才能在主体中用。
cpp
// C++升级struct升级成了类
// 1、类⾥⾯可以定义函数
// 2、struct名称就可以代表类型
// C++兼容C中struct的⽤法
typedef struct ListNodeC
{
struct ListNodeC* next;
int val;
}LTNode;
// 不再需要typedef,ListNodeCPP就可以代表类型
struct ListNodeCPP
{
void Init(int x)
{
next = nullptr;
val = x;
}
ListNodeCPP* next;
int val;
};
定义在类里面的成员函数,编译器默认是内联函数(inline)。
1.2&1.3、访问限定符&类域
类的形成同时伴随着类域的形成,就像是在已有空间上又开辟了一个新的空间,空间与空间之间是有空间壁垒相阻隔的,空间之间想要完成交互,就需要将空间壁垒打破,或是有特有的时空通道连接两空间。所以类域这个空间,其实本身就是封闭的,不与外界空间有通道的。
在类中,有3个访问限定符,分别是public、private和protected,public就像是一条授权的时空通道,但是这条时空通道是被访问空间的内部规则所影响的,public是这条时空通道的起始宽度点,从这个起始开始点开始,第一次遇到他的终止宽度点(private,protected和 };),这始末宽度差就是这条时空通道的真实宽度,而被private和protected所修饰的部分,就像是被访问空间的禁地般,当然,这个禁地的领域范围计算与空间通道计算方式一样,对于禁地来说,设置着作用域访问操作符 (::)这样的令牌,如果有这禁地的访问令牌也是一样可以拜访禁地的。
类域影响的是编译的查找规则。默认情况,编译器编译时,只会在全局域和局部域去查找,类域如果没有建时空通道,类域禁区没有给编译器访问令牌,编译器编译时,是不会去访问的,如此,编译器就像是全局域和局部域的空间之主,类域不是他直接管辖的空间。
cpp
class Stack
{
public:
// 成员函数
void Init(int n = 4);//声明
private:
// 成员变量
int* array;
size_t capacity;
size_t top;
};
// 声明和定义分离,需要指定类域
void Stack::Init(int n)//定义
{
array = (int*)malloc(sizeof(int) * n);
if (nullptr == array)
{
perror("malloc申请空间失败");
return;
}
capacity = n;
top = 0;
}
2、实例化
2.1、实例化概念
用自定义类类型创建的对象就是实例化的具体体现,自定义类就像是一张房子的工程图纸,所有的一切都是纸面上的,不具有实用性,只有通过这个图纸建造出来的房子,也就是对象,他才有实用性,才可以住"人"。当然,通过一张图纸可以建造多个一样户型的房子,同一个自定义类类型可以实例化出多个对象。
自定义类里面的成员对象都只是声明,并没有开空间,只有类实例化出对象时,才会分配空间。

cpp
int main()
{
// Date类实例化出对象d1和d2
Date d1;
Date d2;
return 0;
}
2.2、对象大小
在了解对象大小之前,我们应先弄清楚,用自定义类类型定义的对象中具体包含了什么?类实例化出的每个对象都有独立的数据空间,所以对象中肯定包含成员变量,但是不包含成员函数,函数在编译之后会变成一条汇编指令 [call地址] 存储在代码段中,对象中也不用存储函数指针(函数的地址),因为在编译链接时,编译器就需要找到函数的地址了,而像动态多态,在代码运行时找函数地址,所以需要存储函数地址。

类实例化出的对象同样也要遵守内存对齐的规则,其规则与C语言结构体遵守的内存对齐规则一致,如下:

cpp#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; return 0; }
看完上述代码有没有疑惑,为什么B类和C类并没有成员变量,运行结果会弹1呢?
原因:只是标记一下这个类存在过,弹0,会被误认为B类和C类并没有存在过。
3、this指针
上面提到过,用类类型实例化出的对象并不包含成员函数,成员函数是放在公共代码区的,等待着对象调用,为了方便区别是哪个对象的,就会有一个叫做this的翻译官,有调用时,就把调用对象的身份(对象的地址)给函数。这个翻译官是秘密的,只有在类里面才能显示调用,形参实参不能显示调用this指针,可以理解为函数不想让外人觉得他是废物,还要翻译官帮忙。
this指针默认是形参的第一个参数,是一个当前类类型的指针,类中的成员函数访问成员变量本质是通过this指针访问的,对于this指针,编译器在编译时会自己处理。
cpp
#include<iostream>
using namespace std;
class Date
{
public:
// void Init(Date* const this, int year, int month, int day)
//Date* const this,这个是限定了指针指向的地址不可修改,指向的内容可修改
void Init(int year, int month, int day)
{
// 编译报错:error C2106: "=": 左操作数必须为左值
// this = nullptr;
// this->_year = year;
_year = year;
this->_month = month;
this->_day = day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
// 这⾥只是声明,没有开空间
int _year;
int _month;
int _day;
};
int main()
{
// Date类实例化出对象d1和d2
Date d1;
Date d2;
// d1.Init(&d1, 2024, 3, 31);
d1.Init(2024, 3, 31);
d1.Print();
d2.Init(2024, 7, 5);
d2.Print();
return 0;
}
来点题目试试水:
cpp#include<iostream> using namespace std; class A { public: void Print() { cout << "A::Print()" << endl; } private: int _a; }; int main() { A* p = nullptr; p->Print(); //p是一个类类型对象的指针,Print()是类中的成员函数,并不在对象里面, //所以就不存在对空指针的解引用,算是调用 return 0; }
cpp#include<iostream> using namespace std; class A { public: void Print() { cout << "A::Print()" << endl; cout << _a << endl; //实则出现了对空指针的解引用 // cout << this->_a << endl; } private: int _a; }; int main() { A* p = nullptr; p->Print(); return 0; }
this指针本质是形参,是局部变量。
4、C++和C语言实现Stack对比
类的学习可以让我们感受一下封装~
C++中数据和函数都放到了类⾥⾯,通过访问限定符进⾏了限制,不能再随意通过对象直接修改数 据,这是C++封装的⼀种体现,这个是最重要的变化。这⾥的封装的本质是⼀种更严格规范的管 理,避免出现乱访问修改的问题。
C++中有⼀些相对⽅便的语法,⽐如Init给的缺省参数会⽅便很多,成员函数每次不需要传对象地 址,因为this指针隐含的传递了,⽅便了很多,使⽤类型不再需要typedef⽤类名就很⽅便。



