目录
1. 类的定义
2. 访问限定符
3. 类域
4. 实例化
5. 对象的大小
6. 为什么要内存对齐
7. this指针
1. 类的定义
• class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后⾯分号不能省
略。类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的⽅法或者成员函数。
• 为了区分成员变量,⼀般习惯上成员变量会加⼀个特殊标识,如成员变量前⾯或者后⾯加_ 或者 m 开头,注意C++中这个并不是强制的,只是⼀些惯例,具体看公司的要求。
• C++中struct也可以定义类,C++兼容C中struct的⽤法,同时struct升级成了类,明显的变化是
struct中可以定义函数,⼀般情况下我们还是推荐⽤class定义类。
• 定义在类⾯的成员函数默认为inline。
class student
{//成员函数
public :void init(const char* name, int age, int score){_name = name;_age = age;_score = score;}void print(){cout<<"学生名字:" << _name<<endl<<"学生年龄:" << _age <<endl<<"学生得分:"<< _score << endl;}//成员变量
private:
// 为了区分成员变量,⼀般习惯上成员变量
// 会加⼀个特殊标识,如_ 或者 m开头const char *_name;int _age;int _score;
};
int main()
{student s1;s1.init("czy", 18, 100);s1.print();return 0;
}
c++升级了struct
// 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;
};
//c语言中我们通常这样定义一个结点
typedef struct ListNodeC
{
struct ListNodeC* next;
int val;
}LTNode;
而在c++中我们可以直接如下定义struct ListNodeC
{ListNodeC* next;
int val;
};
//c++这种写法在c的源文件是过不去的
//但c的写法可以在cpp上通过
//也就是说我们不需要在typedef了后面跟的就是该结构体类型
2. 访问限定符
C++⼀种实现封装的⽅式,⽤类将对象的属性与⽅法结合在⼀块,让对象更加完善,通过访问权限
选择性的将其接⼝提供给外部的⽤⼾使⽤。
• public修饰的成员在类外可以直接被访问;protected和private修饰的成员在类外不能直接被访
问,protected和private是⼀样的,以后继承章节才能体现出他们的区别。
• 访问权限作⽤域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为⽌,如果后⾯没有 访问限定符,作⽤域就到 }即类结束。
• class定义成员没有被访问限定符修饰时默认为private,struct默认为public。
• ⼀般成员变量都会被限制为private/protected,需要给别⼈使⽤的成员函数会放为public,这样我们在类外部想要访问我们的成员变量,就要通过我们的成员函数进行操作。这里就体现出了c++的封装性,但是封装性的体现 :更多的是讲类的成员变量和成员函数放一起了,然后通过权限操作符给外部的用户使用这里就不细讲了。
3. 类域
类定义了⼀个新的作⽤域,类的所有成员都在类的作⽤域中,在类体外定义成员时,需要使⽤ :: 作
⽤域操作符指明成员属于哪个类域。
• 类域影响的是编译的查找规则,下⾯程序中Init如果不指定类域Stack,那么编译器就把Init当成全
局函数,那么编译时,找不到array等成员的声明/定义在哪⾥,就会报错。指定类域Stack,就是知
道Init是成员函数,当前域找不到的array等成员,就会到类域中去查找。
#include<iostream>
using namespace std;
class student{//成员函数public :void init(const char* name, int age, int score);void print();//成员变量private:const char *_name;int _age;int _score;};// 声明和定义分离,需要指定类域
void student::init(const char* name, int age, int score)
{{_name = name;_age = age;_score = score;}
}
void student::print()
{cout << "学生名字:" << _name << endl << "学生年龄:" << _age << endl<< "学生得分:" << _score << endl;
}
int main()
{student s1;s1.init("czy", 18, 100);s1.print();return 0;
}
4. 实例化
⽤类类型在物理内存中创建对象的过程,称为类实例化出对象。
• 类是对象进⾏⼀种抽象描述,是⼀个模型⼀样的东西,限定了类有哪些成员变量,这些成员变量只 是声明,没有分配空间,⽤类实例化出对象时,才会分配空间。
• ⼀个类可以实例化出多个对象,实例化出的对象 占⽤实际的物理空间,存储类成员变量。打个⽐
⽅:类实例化出对象就像现实中使⽤建筑设计图建造出房⼦,类就像是设计图,设计图规划了有多
少个房间,房间⼤⼩功能等,但是并没有实体的建筑存在,也不能住⼈,⽤设计图修建出房⼦,房
⼦才能住⼈。同样类就像设计图⼀样,不能存储数据,实例化出的对象分配物理内存存储数据。
#include<iostream>
using namespace std;
class student{//成员函数public :void init(const char* name, int age, int score);void print();//成员变量//这里没有实际的空间只是声明private:const char *_name;int _age;int _score;};// 声明和定义分离,需要指定类域
void student::init(const char* name, int age, int score)
{{_name = name;_age = age;_score = score;}
}
void student::print()
{cout << "学生名字:" << _name << endl << "学生年龄:" << _age << endl<< "学生得分:" << _score << endl;
}
int main()
{//类实例化后s1才有了空间student s1;s1.init("czy", 18, 100);s1.print();return 0;
}
5. 对象的大小
类对象通常是不存函数指针的,成员函数通常放在一个公共代码区,因此我们只要计算成员变量的大小就可以,这里我们补充一下内存对齐的知识:
第⼀个成员在与结构体偏移量为0的地址处。
• 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
• 注意:对⻬数 = 编译器默认的⼀个对⻬数 与 该成员⼤⼩的较⼩值。
• VS中默认的对⻬数为8
• 结构体总⼤⼩为:最⼤对⻬数(所有变量类型最⼤者与默认对⻬参数取最⼩)的整数倍。
• 如果嵌套了结构体的情况,嵌套的结构体对⻬到⾃⼰的最⼤对⻬数的整数倍处,结构体的整体⼤⼩
就是所有最⼤对⻬数(含嵌套结构体的对⻬数)的整数倍。
6. 为什么要内存对齐
我们可以这样理解电脑可能是32位或者64位的这里我们假设是32位的也就是是说每次读取会获取32个比特位也就是四字节,每一个字节都有对应的编号,是从0开始的。下面我们看图
因为我们每次读取四字节如果有了内存对齐我们就可以一次读取到a,不内存对齐就会使我们读两次并且每次读的都是一部分还要合并删减等非常不方便。而内存对齐这样就解决了访问效率问题。
7. this指针
student类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当s1调⽤Init和
Print函数时,该函数是如何知道应该访问的是s1对象还是s2对象呢?那么这⾥就要看到C++给了
⼀个隐含的this指针解决这⾥的问题
编译器编译后,类的成员函数默认都会在形参第⼀个位置,增加⼀个当前类类型的指针,叫做this
指针。
类的成员函数中访问成员变量,本质都是通过this指针访问的,如Init函数中给_name赋值,_name = name;就相当于this->_name = name;
C++规定不能在实参和形参的位置显⽰的写this指针(编译时编译器会处理),但是可以在函数体内显
⽰使⽤this指针。
这里我们注意this指针不是在类对象中存放的,是在栈区有的编译器会把this放在在寄存器中,同时this指针可以为空,只要不通过this指针访问成员变量就不会报错,可以访问成员函数也不会报错,因为通过空的this去调用我们的成员函数时,并没有对this指向的空间进行操作。这里this指向的空间时当前类对象。也就是里面的成员变量。
这里我们就是取d1的地址放到了寄存器。