1.类的定义
类定义在面向对象编程中是一个核心概念,它定义了对象的结构和行为。在C++中,类定义包含类的名称、数据成员(也称为属性或者字段)和成员函数(也称为方法或者操作)多个部分。数据成员定义了对象的状态,而成员函数定义了对象可以执行的操作。下面我们将堆类定义的各个部分进行更加详细的认识。
1.1类名
class为类定义的关键字,类名是一个标识符,用于表示这个类。在C++中,类名通常使用大写字母开头的驼峰命名法来命名。
例如在下面的例子1中类名为Point
1.2访问修饰符
- C++一种实现封装的方式,用类将对象是属性和方法结合在一起,让对象更加完善。通过访问权限选择性 的将其接口提供给外部的用户使用。
- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现为止。如果后面没有访问限定符,作用域就到 } 即类结束。
- class定义成员没有被访问限定符修饰是默认为private,struct默认为public。
C++提供了三种访问修饰符来控制类成员的访问权限:
- public : 成员在类的外部也可以被访问。
- protected: 成员在类的外部不可直接访问,但是在派生类(子类)中可以被访问。
- private : 成员在类的外部和派生类中均不可直接访问,只能通过类的成员函数来访问。
如在下例子1中:private的作用域为private出现的位置到public出现为止之间,public的作用域为public出现的位置到main函数上面最后一个花括号 } 。
例子1:
#include<iostream>
using namespace std;
class Point
{
private:
int x;//点的x坐标
int y;//点的y坐标
public:
//构造函数,用于初始化点的坐标
Point(int x = 0,int y = 0) : x(x),y(y){}
//设置x坐标的成员函数
void setX(int x)
{
this->x = x;
}
//设置y坐标的成员函数
void setY(int y)
{
this->y = y;
}
//获取x坐标的成员函数
int getX()
{
return x;
}
//获取y坐标的成员函数
int getY()
{
return y;
}
};
int main()
{
Point myPoint;//创建一个Point对象,默认坐标为(0,0)
cout << "Initial point: (" << myPoint.getX() << ", " << myPoint.getY() << ")" << endl;
myPoint.setX(5); //设置x的坐标为5
myPoint.setY(10); //设置y的坐标为10
cout << "Updated point: (" << myPoint.getX() << ", " << myPoint.getY() << ")" << endl;
return 0;
}
1.3类域
在C++中,类域是指类中定义的成员(包括变量和方法)的可见性和可访问性范围。类域是面向对象编程中的一个核心概念,它有助于实现封装、隐藏类的实现细节,并提供清晰的接口共外部使用。
1.3.1类域的定义
类域从类的左大括号 { 开始,到右大括号 } 结束。在这个范围内声明的所有成员都属于类域。
1.3.2:成员的可见性
在类域中声明的成员可以在类的内部被访问,但是它们的外部可访问性受到访问限定符(public、protected、private)的控制
如在例子2中:私有成员name、age不能被直接访问
class Person
{
public:
void printInfo()//公有成员函数
{
cout << name << ", " << age << endl;
}
private:
string name;//私有成员变量
int age;//私有成员变量
};
int main()
{
Person person;
person.name = "Alice";//错误:不能直接访问私有成员
person.age = 20;//错误:不能直接访问私有成员
person.printInfo();//正确:通过公有成员函数访问私有成员
return 0.;
}
注意:定义在类里面的成员函数默认为inline。C++中struct也可以定义类,C++兼容C中struct的用法,同时struct升级成了类,明显的变化是struct中可以定义函数,一般情况下我们还是推荐用class定义类。
2.实例化
2.1实例化概念
- 用类类型在物理内存中创建对象的过程,称为类实例化处对象。
- 类是对象进行一种抽象描述,限定了类有那些乘以变量,这些成员变量只是声明没有分配空间,用类实例化处对象时才会分配空间。
- 一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量。就好比一张房子设计图纸是类,用图纸建筑出很多房子就是实例化。
如下面例子3中:Point实例化出对象p1和p2
例子3:
class Point
{
private:
int x, y;//成员变量
public:
//构造函数
Point(int a = 0,int b = 0) : x(a), y(b) { }
//成员函数
void print()
{
cout << "(" << x << ", " << y << ")" << endl;
}
};
int main()
{
//实例化Point类,创建Point对象
Point p1(1, 2); //使用构造函数初始化x和y
Point p2; //使用默认构造函数,x和y都被初始化为0
//调用成员函数
p1.print(); //输出:(1,2)
p2.print(); //输出:(0,0)
return 0;
}
2.2对象大小
在C++中,类对象的大小取决于多个因素,包括类成员的类型、大小、数量以及成员变量的对齐要求。类的成员函数(方法)通常不直接计入对象的大小,因为成员函数在内存中是以代码的形式存在的,与对象实例的内存布局是分开的。但是成员函数中的任何静态成员变量会独立于对象实例存在,并且其大小也不会计入单个对象的大小中而是像全局变量一样存在于程序的静态数据区。总而言之,类对象大小的计算原则和结构体的大小计算原则一样。
计算结构体大小的对齐原则:
- 第一个成员在与结构体偏移量为0的地址处。
- 其他成员变量要对齐到其对应对齐数的整数倍的地址处。
- 对齐数 = 编译器默认对齐数与该成员大小的较小值。
- VS的默认对齐数为8
- 结构体的总大小为:最大对齐数(所有变量类型的最大者与默认对齐数最小)的整数倍。
- 可以参考文章:C语言:结构体、共用体和枚举(1)-CSDN博客
如在例子4中:类MyClass的大小为16。这是因为char本身大小为1,VS编译器默认大小为8,较小值为1,所以对齐数为1,占1个字节大小;int本身大小为4,VS编译器默认大小为8,较小值为4,所以对齐数为4,占4个字节大小;double本身大小为8,VS编译器默认大小为8,较小值为8,所以对齐数为8,占8个字节大小。
例子4:
class MyClass
{
public:
char a; // 1 8 1
int b; // 4 8 4
double c;// 8 8 8
};
int main()
{
cout << " Size of MyClass: " << sizeof(MyClass) << " bytes" << endl;
return 0;
}
例子5:
class A
{
public:
void print()
{
cout << ch << endl;
}
private:
char ch;
int i;
};
int main()
{
cout << "Size of A: " << sizeof(A) << " bytes" << endl;
return 0;
}
由上面例子5:我们可以知道类的成员函数是不计入类大小的计算中的。
类A的大小为8。这是因为char本身大小为1,VS编译器默认大小为8,较小值为1,所以对齐数为1,占1个字节大小;int本身大小为4,VS编译器默认大小为8,较小值为4,所以对齐数为4,占4个字节大小.
例子6:
class B
{
public:
void Print()
{
//...
}
};
int main()
{
cout << "Size of B: " << sizeof(B) << " bytes" << endl;
return 0;
}
在上面例子6:我们可以看到没有成员变量的类B的大小为1,为什么没有成员变量还是1字节呢?因为如果一个字节都不给怎么表示对象存在过呢!所以这里给1字节是为了占位标识对象存在。
3.this指针
- 编译器 编译后,类的成员函数默认都会在形参第一个位置增加一个当前类类型的指针,叫做this指针。
- 类的成员函数中访问成员变量,本质都是通过this指针访问的。
- C++规定不能在实参和形参的位置显示的写this指针(编译时编译器会处理),但是可以在函数体内显示使用this指针。
2.3.1主要用途
- 区分成员变量和局部变量:当成员变量和局部变量的名字相同时,可以通过this->成员变量名来明确指定是访问类的成员变量。如在下面例子
- 返回当前对象的引用:在需要返回当前引用的函数中,可以使用return *this来实现链式调用。
class Box
{
public:
double width;
void setWidth(double width)
{
this->width = width;//使用this指针来区别成员变量width和参数width
}
};