🎯引言
在面向对象编程(OOP)中,类是构建对象的蓝图或模板。它不仅定义了对象的属性和行为,还为我们提供了创建多个相似对象的能力。在C++中,理解类的定义、对象的实例化以及 this
指针的使用是迈向高级编程的关键一步。本篇博客将带你深入了解这些基本概念,帮助你为更复杂的编程任务打下坚实的基础。
👓类和对象(上)
1.类的定义
1.1类的定义格式
class ClassName {
// 访问权限说明符:(下面会仔细讲解)
public:
// 公有成员变量和成员函数
private:
// 私有成员变量和成员函数
protected:
// 受保护成员变量和成员函数
// 其他成员声明...
};
类名 (ClassName)
类名是你定义的类的名称。类名通常使用大写字母开头的命名规则,以便与普通变量区分开来。类名的选择应当清晰、具有描述性,反映类所代表的概念或实体。
成员变量 (Member Variables)
成员变量(也称为数据成员)是类中定义的属性或数据。它们描述了类的状态或特征。成员变量通常放在 private
或 protected
区域,以实现数据封装和信息隐藏。
成员函数 (Member Functions)
成员函数(也称为方法)是定义类行为的函数。成员函数可以访问类的成员变量,并实现对象的操作逻辑。成员函数通常放在 public
区域,以便类外部调用。
1.2访问限定符
1. public
访问限定符
- 定义: 使用
public
关键字声明的成员可以被类外部直接访问。 - 用途: 通常用于定义类的接口部分,即那些需要被类外部代码使用的成员。
- 特点:
- 所有
public
成员在类的对象被创建后,可以通过对象名直接访问。- 继承时,派生类可以访问基类的
public
成员。(初学CPP的可以不用关)
- 继承时,派生类可以访问基类的
- 所有
示例:
class Rectangle {
public:
int width; // 公有成员变量
int height; // 公有成员变量
int area() { // 公有成员函数
return width * height;
}
};
int main()
{
Rectangle rect;
rect.width = 5; // 直接访问公有成员变量
rect.height = 10; // 直接访问公有成员变量
int area = rect.area(); // 直接调用公有成员函数
return 0;
}
2. private
访问限定符
- 定义: 使用
private
关键字声明的成员只能在类的内部访问,类外部无法直接访问这些成员。 - 用途: 用于数据隐藏和封装,将不希望被类外部直接访问的成员声明为
private
,如内部数据和辅助函数。 - 特点:
- 通过类的公有函数在类的外部来访问
private
成员。 - 继承时,派生类不能直接访问基类的
private
成员。
- 通过类的公有函数在类的外部来访问
示例:
class Rectangle {
private:
int width; // 私有成员变量
int height; // 私有成员变量
public:
void setDimensions(int w, int h) { // 公有成员函数,用于设置私有变量
width = w;
height = h;
}
int area() { // 公有成员函数,访问私有变量
return width * height;
}
};
int main()
{
Rectangle rect;
//rect.width; 这段代码时错误的,私有成员不能在类外访问
rect.setDimensions(5, 10); // 通过公有成员函数设置私有变量
int area = rect.area(); // 通过公有成员函数访问私有变量
return 0;
}
3.protected
访问限定符
当前章节,只需知道与private
访问限定符的用途差不多即可。
注:
class定义成员没有被访问限定符修饰时默认为private,struct默认为public。
#include <iostream>
using namespace std;
class Example1
{
int a;
int b;
};
struct Example2
{
int a;
int b;
};
int main()
{
Example1 a1;
Example2 a2;
//a1.a = 10;该段代码是错误的
a2.a = 10;//改代码正确
}
1.3类域
类域指的是一个类的作用范围。它定义了在类中声明的成员变量和成员函数的可见性和作用范围。类的成员只能在类的内部(或通过类的对象)访问,除非它们被声明为 public
,允许在类的外部使用。
类中声明成员,但在类外定义成员时,需要使用::域操作符,指明成员属于哪个类域
示例:
#include <iostream>
using namespace std;
class Example1
{
private:
int a;
int b;
public:
void print();
};
void Example1::print()
{
cout << "print()" << endl;
}
int main()
{
Example1 a;
a.print();
return 0;
}
2.实例化
2.1实例化概念
1. 什么是对象实例化
对象实例化是从类创建一个对象的过程。在这个过程中,程序分配内存以存储对象的数据,并调用类的构造函数来初始化对象的状态。每次实例化都会产生一个新的对象,它们独立于其他对象,尽管它们的行为和特性基于同一个类。
2. 类与对象的关系
我们建造一个房子,是不是需要建造的结构图纸,这个结构图纸就等于是类,对象,就等于是通过图纸建造出来的房子
- 类(Class):
类是对一类对象的抽象描述,它定义了对象的属性(成员变量)和行为(成员函数)。类本质上是一个蓝图或模板。 - 对象(Object):
对象是类的一个具体实例。通过实例化类,你可以创建对象,每个对象都有属于自己的数据和行为。
3.对象实例化的过程
在C++中,对象实例化通常通过以下步骤完成:
- 定义类: 首先,你需要定义一个类,其中包含成员变量和成员函数。
- 声明对象: 使用类名来声明一个对象。对象声明时,系统会自动调用类的构造函数来初始化对象。
示例:
#include <iostream>
using namespace std;
class Car {
public:
int year;
// 公有成员函数,用于显示对象的信息
void displayInfo() const {
cout << "Year: " << year << endl;
}
};
int main() {
// 实例化对象
Car myCar;
myCar.year=10;
// 调用对象的方法
myCar.displayInfo();
return 0;
}
2.2对象大小
1. 对象大小的基本概念
- 对象的大小是指在内存中分配给该对象的总字节数,包括它的所有成员变量。
- 对象的大小通常是成员变量大小的总和,但可能因为对齐要求而有所增加。
- 对象的大小不包括成员函数
2.计算对象大小
对齐要求(和结构体计算是相似的)
为了提高内存访问效率,编译器会对数据进行对齐。对齐要求可能导致对象的实际大小大于其成员变量大小的总和。对齐是为了使数据在内存中按特定的边界对齐,这样可以提高访问速度。
lass Aligned {
char a; // 1 byte
int b; // 4 bytes (通常对齐到4字节边界)
};
在这个例子中,int
可能会被对齐到4字节边界,因此 char
和 int
之间可能会有填充字节。实际对象的大小可能是 1 + 3 (填充) + 4 = 8 bytes
。
3.this指针
this指针在我们平时使用的时候是隐藏起来的,下面我将通过一段代码让你发现this指针的存在
class Fruit
{
public:
void show(int x)
{
a = x;
cout << a << endl;
}
private:
int a;
};
int main()
{
Fruit a;
Fruit b;
a.show(10);
b.show(20);
return 0;
}
该段程序将会输出
10
20
程序中这段代码a并没有明确的指向,我们赋值的的时候,怎么知道这个a是对象a中的a还是对象b中的a呢?
其实这里隐藏this指针,this指针是指向调用成员函数的对象
void show(int x)
{
a = x;
cout << a << endl;
}
也就是是他的实际代码是
void show(Fruit* this,int x)
{
this->a = x;
cout << a << endl;
}
const Fruit* this
this->a
这写代码编译器优化了,无需再写,由于一些设定,Fruit* this
无法写入参数列表中,大家需要注意,
但不过this可以显示的写出来也就是this->a = x;
这段代码中this可以写出来
this
指针的特点
- 指向当前对象:
this
指针指向调用成员函数的对象。 - 常量指针:
this
指针是一个隐式的常量指针,不能修改其指向,即不能给this
指针赋值。 - 在成员函数中可用:只能在非静态成员函数中使用
this
指针,静态成员函数中没有this
指针。 - 用于返回对象本身:常用于返回对象本身的引用,从而实现链式调用。
示例
#include <iostream>
class MyClass {
public:
MyClass(int value) : data(value) {}
// 使用 this 指针来访问对象的成员变量
void displayData() const {
std::cout << "Data: " << this->data << std::endl;
}
// 使用 this 指针来返回对象本身,实现链式调用
MyClass& setData(int value) {
this->data = value;
return *this;
}
private:
int data; // 私有数据成员
};
int main() {
MyClass obj(10);
// 使用 this 指针访问成员变量
obj.displayData();
// 使用 this 指针实现链式调用
obj.setData(20).displayData();
return 0;
}
this
指针的具体用处
- 访问对象的成员变量和成员函数:在成员函数中,可以通过
this
指针访问对象的成员变量和成员函数。 - 返回对象本身:成员函数可以返回
*this
,即返回调用该函数的对象本身。这对于实现链式调用非常有用。 - 避免名称冲突:在成员函数中,如果函数参数的名称与成员变量的名称相同,可以使用
this
指针来区分它们。
详细解释
1. 访问对象的成员变量和成员函数
在成员函数中,可以使用this
指针来访问对象的成员变量和成员函数。例如:
void MyClass::displayData() const {
std::cout << "Data: " << this->data << std::endl;
}
这里,this->data
表示当前对象的data
成员变量。
2. 返回对象本身
为了实现链式调用,可以在成员函数中返回*this
,即返回调用该函数的对象本身。例如:
MyClass& MyClass::setData(int value) {
this->data = value;
return *this;
}
这样可以实现如下的链式调用:
obj.setData(20).displayData();
3. 避免名称冲突
如果成员函数的参数名称与成员变量名称相同,可以使用this
指针来区分。例如:
class MyClass {
public:
MyClass(int data) {
this->data = data; // 使用 this 指针区分成员变量和参数
}
private:
int data;
};
这里,this->data
表示成员变量,而data
表示函数参数。
this
指针在常量成员函数中的使用
在常量成员函数中,this
指针的类型是const MyClass*
,即指向常量对象的指针。这意味着在常量成员函数中,不能修改对象的成员变量或调用非常量成员函数。例如:
void MyClass::displayData() const {
std::cout << "Data: " << this->data << std::endl;
// this->data = 20; // 错误:不能在常量成员函数中修改数据成员
}
总结
this
指针是一个隐含于每一个非静态成员函数中的指针,指向调用成员函数的对象。- 它是一个常量指针,不能修改其指向。
- 可以在成员函数中使用
this
指针访问对象的成员变量和成员函数,返回对象本身,实现链式调用,以及避免名称冲突。 - 在常量成员函数中,
this
指针的类型是const MyClass*
,不能修改对象的成员变量或调用非常量成员函数。
🥇结语
掌握类的定义、对象的实例化以及 this
指针的使用,是精通C++编程的基础。这些概念不仅增强了代码的组织性和可读性,还为实现复杂的对象间交互提供了强大的工具。希望通过本篇博客的讲解,你对这些核心概念有了更加清晰的理解,并能在实际编程中灵活运用它们。