类
什么是类
当前由于知识的浅薄。所以我们将以最简单的认知来认识C++中的知识,如有错误,请原谅。
在当前我浅薄的认知中,类就是C中结构体进行的升级
类是可以在struct中定义函数。,但是C++中使用class。
struct Stack
{
void Init(size_t capacity)//在结构体中定义函数
{
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}
_capacity = capacity;
_size = 0;
}
DataType* _array;
size_t _capacity;
size_t _size;//一般类中私有成员函数建议前面加_,方便与其他参数成员作区分
};
类的定义
class className
{
// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
类中分为成员变量和成员函数
class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分
号不能省略。(和结构体相似)
类的定义方式一般有两种:声明和定义全部放在类体中,类声明放在.h文件中,成员函数定义放在.cpp文件中。
前者缺点是编译器容易把成员函数的定义认为是内联。
后者是需要成员函数名前需要加类名和::
#include”xxx.h“
void className::Add()//加类名
{
//...
}
访问限定符
类中增加了访问限定符:public、protect、private
而public和protect 是公有,可以在域外进行访问。private 是私有,只有域内可以访问
struct 是默认为公开的,而类是默认私有的
访问限定符的说明:
- public修饰的成员在类外可以直接被访问
- protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
- 如果后面没有访问限定符,作用域就到 } ,即类结束。
- class的默认访问权限为private,struct为public(因为struct要兼容C)
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
类的作用域
类定义了一个新的作用域, 类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 ::
作用域操作符指明成员属于哪个类域。
class Date
{
public:
void Yearday();
private://public的作用域到此为止
int _year;//只是声明
int _month[12];
int _age;
};
void Date::Yearday()//属于Date这个类
{
cout << _year<< _month<<_day << endl;
}
类的实例化
实例化定义
C++是要兼容C语言的,所以类中也是可以用struct定义类的。
那么说明类其实也是可以查看自身的大小,但是由于类本身就是一个抽象化的概念,所以我们无法直接通过类的成员来判定大小。好比设计图与设计出来的实物,类就是设计图,而设计出实物这个过程我们称之为类的实例化。
说白了,实例化就是开空间。
用类类型创建对象的过程,称为类的实例化
一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
int main()
{
Date._month = 10; // 编译失败:error C2059: 语法错误:“.”
return 0;//Date类是没有空间的,只有Date类实例化出的对象才有具体的年龄
}
实例化后对象的大小
成员变量在对象中,成员函数不在对象中,因为每个成员成员变量是不同的,需要独立存储,但是成员函数是一样的,所以放到了公共区域(代码段)。
相当于成员变量是自家的房子,而成员函数类似于公共休息区一样。
注意:实例化后的对象大小也要遵守对齐规则。
这几个类的字节大小是多少?
// 类中既有成员变量,又有成员函数
class A1 {
public:
void f1(){}
private:
int _a;//4
};
// 类中仅有成员函数
class A2 {
public:
void f2() {}//1
};
// 类中什么都没有---空类
class A3
{};//1
A1只计算成员变量的大小所以是4字节,A2和A3分别是1字节,只表示占位,没有实例化前是1。
附录对齐规则
- 第一个成员在与结构体偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的对齐数为8 - 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
this指针
由于我们使用在代码段的公共成员函数,所以为了便于我们区分,C++中使用this指针来区分不同使用
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;//声明
int _month;
int _day;
};
int main()
{
Date d1;
Date d2;
d1.Init(2022, 1, 11);
d2.Init(2022, 1, 12);//如何区分的d1,d2
d1.Print();
d2.Print();
return 0;
}
C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
所以实际上编译器会把上述代码更改成为
class Date
{
public:
void Init(Date* this,int year, int month, int day)
{
this->_year = year;
this->_month = month;
this->_day = day;//都不需要我们自己加,编译器会处理
}
private:
int _year;//声明
int _month;
int _day;
};
d1.Init(&d1,2022, 1, 11);
d2.Init(&d2,2022, 1, 12);//这是编译器自动处理,不需要我们额外加参数
所以,this指针其实是存在栈中的隐含形参。
关于this指针能否为空的分析:
class A
{
public:
void Print()
{
cout << "Print()" << endl;
}
void PrintA()
{
cout << _a << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();//编译正确
//p->PrintA();//崩溃
(*p).Print();//编译正确
return 0;
}
并不是解引用P,就会程序崩溃。由于类的成员函数是存在代码段的,所以程序会去代码段寻找函数的地址,并没有使用空指针的解引用操作。所以并不会发生程序崩溃。
在寻找类中的成员变量时,会查找该成员变量的地址,所以传递空指针时,会对其进行解引用操作,而对我们无法对空指针进行解引用操作。所以程序崩溃。