什么是类,什么是对象?
对象(就是一个实体,但计算机识别不了实体,所以让计算机认识实体,就需要把实体抽象成一个对象)
类相当于类型,类是对对象的描述
1.类的引入
C语言中,结构体只能定义变量(数据和数据操作的方法是分离开的),在C++中,结构体内不仅可以定义变量,也可以定义函数
代码1
struct Stu
{
//定义函数
void InitStudent(char* name, int age, char* gender)
{
strcpy(_name, name);
_age = age;
strcpy(_gender, gender);
}
void PrintStudent()
{
cout << _name << " " << _age << " " << _gender << " " << endl;
}
//定义变量
char _name[20];
int _age;
char _gender[10];
};
C++中更喜欢用class来代替
2.类的定义
class为定义类的关键字
class className//关键字+类的名字
{
//类体:由成员函数和成员变量组成
};
两种定义方式:
1.声明和定义全部放在类体中,注:成员函数如果在类中定义,编译器可能会将其当成内联函数处理
2.声明放在.h文件中,类的定义放在.cpp文件中//一般采取第二种方式,需要用时,包括头文件就好啦
代码2
test.h//类的声明
class Stu
{
public:
void InitStudent(char* name, int age, char* gender);
void PrintStudent();
private:
char _name[20];
int _age;
char _gender[10];
};
test.cpp//类的定义
#include "test.h"
//要说明PrintStudent是Stu类里的成员函数 类名+作用域限定符(::)
void Stu::InitStudent(char* name, int age, char* gender)
{
strcpy(_name, name);
_age = age;
strcpy(_gender, gender);
}
void Stu::PrintStudent()
{
cout << _name << " " << _age << " " << _gender << " " << endl;
}
int main()
{
Stu s;
s.InitStudent("张三", 15, "男");
s.PrintStudent();
system("pause");
return 0;
}
3.类的访问限定符及封装
细心的人会发现,在代码2的.h代码里,声明变量和函数前加了public、private这样的访问限定符,接下来就要谈谈类的访问限定符及封装了
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。也就是说,在类中,我们使用访问限定符(比如pubic、private等),隐藏内部的实现细节,暴露一些公有的方法与其他对象进行交互
封装本质上是一种管理:我们用类将对象的数据和方法封装一下,和外部有交流的用public封装,不想给别人看到的,用private和protected把成员封装起来。C++实现封装是通过class+访问权限
访问限定符:public(公有)、protected(保护)、private(私有)
注:
- 1.public修饰的成员在类外可以直接被访问
- 2.protected和private修饰的成员在类外不能直接被访问
- 3.访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
- 4.class的默认访问权限为private,struct为public(因为struct要兼容C)
- 访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
问题:C++中struct和class的区别是什么?
因为C++需要兼容C语言,所以C++中struct可以当结构体使用。另外C++还可以定义类,和class定义类是一样的。区别是默认访问权限不同,struct是public(公有的),class是private(私有的);struct在模板参数列表中不能修饰类型,而class可以。
4.类的实例化
用类类型创建对象的过程称为类的实例化
int main()
{
Stu s;//用类Stu创建对象s,该过程就是类的实例化,所有的操作都是在对象上进行操作,而不是对类进行操作。类本身操作不了,它只是一个描述
return 0;
}
- 类只是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间
- 一个类可以实例化出多个对象,实例化出的对象,占用实际的物理空间,存储类成员变量
- 类只是一个设计,实际存不了什么东西,比如设计图;类实例化出的对象才能实际存储数据,就像是用设计图建造出的房子
4.1 类中对象的存储方式
每个对象中成员变量是不同的,但是调用同一份函数,所以有如下存储方式:
方式1:变量和函数放在一起
缺陷:按照此种方式,当一个类创建多个对象时,每个对象都会保存一份代码,相同代码保存多次,浪费空间
方式2:只保存成员变量,成员函数存放在公共的代码段,定义指针指向公共代码区
最后,通过计算类对象的大小(见4.2),可以发现,类对象存储中,不是方式1,也不是方式2,而是方式三:只包含了成员变量
4.2 如何计算类的大小?
在C语言中,怎么计算结构体的大小---------结构体的内存对齐
C++中:就是结合内存对齐,再将该类中成员变量加起来
class A
{
public:
void f1(){}
protected:
int _a;
};
class B
{
public:
void f2(){}
};
class C
{
};
int main()
{
cout << sizeof(A) << endl;//4 可以看出,类中有成员变量和方法时,大小跟成员方法无关
cout << sizeof(B) << endl;//1 只有成员方法,没有成员变量,也是1
cout << sizeof(C) << endl;//1 空类的大小是1,
//如果占0个字节,创建出的对象会在同一个位置,有矛盾,所以,占了一个字节可以区分类创建出的对象
system("pause");
return 0;
}
结论:计算类的大小,将类中成员变量加起来,同时也要结合内存对齐
特例:
- 类中只有成员方法,没有成员变量,大小为1(原因同空类)
- 空类的大小是1(在主流的编译器里,比如VS),不是0:是因为,如果是0,然后再创建若干个对象,那么这若干个对象会在同一位置,同一位置则是同一个对象,与条件有矛盾,所以,让空类占一个字节,就可以把对象放在不同的内存单元里,就可以区分创建出的对象
附:求结构体中成员变量相对于起始位置的偏移量用offsetof
第一个参数是结构体名称,第二个参数是要求的那个成员变量
用预处理命令进行内存对齐 #pragma pack(8)//按8字节内存对齐
class A
{
public:
void f1(){}
int _a;
double _b;
};
int main()
{
cout << sizeof(A) << endl;//16 类的大小为16,结合内存对齐
cout << offsetof(A, _a) << endl;// 0 第一个成员变量,所以为0
cout << offsetof(A, _b) << endl;//8
system("pause");
return 0;
}
(附:怎么模拟实现offsetof)?
5.this指针
在一个类中,如果成员函数多,那么成员函数如何知道操作哪个对象?
C++中通过引入this指针解决该问题,即:C++编译器给每个“成员函数”增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员边框的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
this指针的特性
- 1.this指针的类型:类类型* const(说明this指针的指向不能被修改,指向的是类内的对象)
eg:this指针类型:Student *const
- 2.只能在“成员函数”内部使用
- 3.this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
- 4.this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递
PS: this指针要做的事:
- 1.识别类名//比如 姓名
- 2.识别类中的成员变量;//基本信息
- 3.识别类中的成员函数&并对成员函数进行改写//特长
因为this先识别成员变量,所有类里面的成员变量可以在任意的函数里用,不论变量在类的前面还是后面(成员变量是一个全局变量)
eg:
class Student
{
public:
void InitStudent(Student* const this, char* name, char* gender, int sex)
{
strcpy(this->_name, name);
strcpy(this->_gender, gender);
this->_age = age;
}
void PrintStudent(Student* const this)
{
cout << this->_name << " " << this->_gender << " " << this->_age << endl;
}
private:
char _name[20];
char _gender[5];
int _age;
};
int main()
{
Student s1;
Student::InitStudent(&s1, "zhangsan", "男", 15);
Student::PrintStudent(&s1);
system("pause");
return 0;
}
(调用约定是?)
问题:this指针可以为空吗?
可以为空,但在成员函数访问成员变量,代码会崩溃,因为this为空,而成员变量的访问是通过this来访问,所以一般不为空,但可以为空
如下:
成员函数里没有访问成员变量,代码就可以运行
class A
{
public:
void Test()
{
cout << this << endl;
cout << "Test()" << endl;
}
protected:
int _a;
};
int main()
{
A* ps = nullptr;
ps->Test();
system("pause");
return 0;
}
但如果成员函数加了一条访问成员变量的语句,程序则会崩溃,
因为代码是this->_a=20;只是把this省略了,它指向类内的对象,而对象是有地址的,所以走到那程序崩溃了,如下图: