💗个人主页💗
⭐个人专栏——C++学习⭐
💫点击关注🤩一起学习C语言💯💫
目录
- 导读
- 1. 面向对象
- 2. 类
- 2.1 类的定义
- 3. 类的访问限定符
- 4. class与struct定义类的区别
- 5. 类的封装
- 6. 类的作用域
- 7.类的实例化
- 8. 类的存储方式
- 9. this指针
- 10. 题目思考
导读
前面我们学习了C++的入门基础,感兴趣的小伙伴可以点击上方的“C++学习”专栏。今天我们一起来学习类和对象,本篇文章重点了解类。
1. 面向对象
C++是一种面向对象的编程语言,面向对象编程(OOP)是一种软件开发方法,其中程序被组织为对象的集合,这些对象通过相互之间的通信来完成任务。
比如我们洗衣服:
- 将衣服放入洗涤桶,倒入适量水与洗衣液,开启定时开关。
- 洗衣机定时关闭后放入清水中清掉泡沫放入脱水机中定时脱水。
- 脱水完成后拿出衣服即可。
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
总共有四个对象:人,洗衣机,衣服,洗衣液。
整个过程是四个对象之间交互完成的,人不需要关注洗衣机是如何洗衣甩干的。
2. 类
类是一种自定义的数据类型,可以定义多个对象(实例)来访问类的成员。类的成员可以是变量(数据成员)或函数(成员函数),用于表示对象的状态和行为。
2.1 类的定义
要定义一个类,需要使用关键字class,然后在类体内定义数据成员和成员函数。类体内也可以定义构造函数、析构函数、访问控制修饰符(public、private、protected)等。
class className
{
// 类体:由成员函数和成员变量组成
};
// 一定要注意后面的分号
在定义类中:
- 我们可以把声明和定义全部放在类体中。
需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。
class Person {
public:Person(string name, int age) {this->name = name;this->age = age;}void display() {cout << "Name: " << name << endl;cout << "Age: " << age << endl;}string name;int age;
};int main() {// 创建一个Person对象Person p("Alice", 25);// 调用成员函数display显示对象的信息p.display();return 0;
}
- 类声明放在.h文件中,成员函数定义放在.cpp文件中。
注意:成员函数名前需要加类名::
一般情况下,更期望采用第二种方式。
今天为了方便理解,我们采用第一种方式。
3. 类的访问限定符
在C++中,类的成员可以通过访问限定符来控制其对外部的可见性和访问权限,以确保数据的安全性和封装性。
class MyClass {private:int privateVar;public:int publicVar;protected:int protectedVar;
};
-
public访问限定符:被声明为public的成员可以在类的内部和外部被访问。
-
private访问限定符:被声明为private的成员只能在类的内部被访问,外部代码无法直接访问。
-
protected访问限定符:被声明为protected的成员在类的内部可以被访问,并且在派生类中也可以被访问。
-
访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
-
如果后面没有访问限定符,作用域就到}即类结束。
-
class的默认访问权限为private,struct为public(因为struct要兼容C)
4. class与struct定义类的区别
在C++中,class和struct都可以用于定义自定义的数据类型,即用户自定义的数据结构。它们的主要区别在于默认的成员访问限定符和默认的继承访问限定符。
在class中,默认的成员访问限定符是private。在struct中,默认的成员访问限定符是public。
class MyClass {int privateVar; // 默认为privatepublic:int publicVar;void myFunction() {// 在类的内部可以访问private和public成员privateVar = 10;publicVar = 20;}
};struct MyStruct {int publicVar; // 默认为publicprivate:int privateVar;void myFunction() {// 在结构体的内部可以访问private和public成员privateVar = 10;publicVar = 20;}
};int main() {MyClass myClassObj;myClassObj.publicVar = 30; // 可以直接访问public成员MyStruct myStructObj;myStructObj.publicVar = 40; // 可以直接访问public成员return 0;
}
5. 类的封装
面向对象的三大特性:封装、继承、多态。
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
封装本质上是一种管理,让用户更方便使用类。
class Circle {
private:double radius; // 封装的数据public:// 公共接口void setRadius(double r) {if (r >= 0)radius = r;}double getRadius() {return radius;}double calculateArea() {return 3.14 * radius * radius;}
};int main() {Circle c;c.setRadius(5.0); // 通过公共接口设置半径double area = c.calculateArea(); // 通过公共接口计算面积return 0;
}
在上面的例子中,类Circle封装了一个私有的成员变量radius,并提供了公共的方法setRadius、getRadius和calculateArea来设置半径、获取半径和计算面积。
外部代码无法直接访问和修改radius,只能通过公共接口来操作。这样可以保证radius的有效性和数据的一致性,并提供了简洁的接口来操作数据。
6. 类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。
class Person {
private:string name;int age;public:Person(string name, int age) {this->name = name;this->age = age;}void display();};
// 这里需要指定display是属于Person这个类域
void Person::display()
{cout << "Name: " << name << endl;cout << "Age: " << age << endl;
}
int main() {// 创建一个Person对象Person p("Alice", 25);// 调用成员函数display显示对象的信息p.display();return 0;
}
7.类的实例化
在C++中,类的实例化是通过创建对象来实现的。
如下述代码中,Person类是没有空间的,只有Person类实例化出的对象才有具体的物理空间。
class Person {
private:string name;int age;public:Person(string name, int age) {this->name = name;this->age = age;}void display(){cout << "Name: " << name << endl;cout << "Age: " << age << endl;}};int main() {// 创建Person类对象Person p("Alice", 25);// 调用成员函数display显示对象的信息p.display();return 0;
}
8. 类的存储方式
只保存成员变量,成员函数存放在公共的代码段
9. 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, d2;d1.Init(2024, 1, 30);d2.Init(2022, 1, 29);d1.Print();d2.Print();return 0;
}
Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
:C++编译器给每个“非静态的成员函数“增加了一个隐藏
的指针参数,也就是this指针。
让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。
只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
this指针的使用有以下几个特点:
-
在类的非静态成员函数中,this指针被自动地传入函数中,无需显式地声明。可以通过this指针来访问当前对象的成员变量和成员函数。例如,如果在一个成员函数中想要访问成员变量x,可以使用this->x。
-
this指针是一个常量指针,不能被修改,即不能对this指针进行赋值操作。
-
this指针可以在成员函数中使用,也可以在成员函数外部使用。在成员函数外部使用时,需要通过对象名来访问this指针,例如obj->this。
-
在类的静态成员函数中,由于静态成员函数不属于任何对象,所以不能使用this指针。静态成员函数只能访问静态成员变量和其他静态成员函数。
10. 题目思考
- 下面程序编译运行结果是?
A、编译报错 B、运行崩溃 C、正常运行
class A
{public:void Print(){cout << "Print()" << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->Print();return 0;
}
正常运行
p不发生解引用,因为成员函数的地址不存在对象中,在公共代码区域。这里p为空指针传入print中,然后this接受print地址直接输入。
- 下面程序编译运行结果是?
A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:void PrintA(){cout << _a << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->PrintA();return 0;
}
运行崩溃:前面一样,this接受的是_a,因为-a是成员变量会对print的p进行解引用