C++的面向对象学习(3):面向对象编程的三大特性之:封装

发布时间:2023年12月18日


前言:C++具有面向对象编程的特性

在面向对象编程中,类(Class)和对象(Object)是两个重要的概念。

类是一种用户自定义的数据类型,用于封装数据和函数成员。它是一种模板或蓝图,描述了对象的属性和行为。类定义了对象的结构和行为,并提供了一种创建对象的方式

对象是类的实例,通过实例化类来创建的。它是类的具体化,具有类定义的属性和行为。对象是程序中的实体,可以通过对象来访问和操作类的成员。

举个例子,假设我们有一个类叫做"Car",它描述了汽车的属性和行为。在这个类中,我们可以定义数据成员如"color"、“brand"和"speed”,以及成员函数如"start()"、"accelerate()“和"stop()”。当我们实例化这个类时,就创建了一个具体的汽车对象,可以设置它的颜色、品牌和速度,以及调用它的行为函数。

class Car {
  private:
    string color;
    string brand;
    int speed;

  public:
    void start();
    void accelerate();
    void stop();
};

int main() {
  Car myCar; // 创建一个名为myCar的汽车对象
  myCar.color = "Red"; // 设置汽车对象的颜色
  myCar.brand = "Toyota"; // 设置汽车对象的品牌
  myCar.speed = 0; // 设置汽车对象的速度

  myCar.start(); // 调用汽车对象的启动函数
  myCar.accelerate(); // 调用汽车对象的加速函数
  myCar.stop(); // 调用汽车对象的停止函数

  return 0;
}

在上面的例子中,"Car"类描述了汽车的属性和行为,"myCar"对象是"Car"类的一个实例,我们可以通过对象来访问和操作类的成员。

类和对象是面向对象编程的核心概念,它们提供了一种组织和封装代码的方式,使代码更具可读性、可维护性和可扩展性。

一、根据前言的例子可以看出,对一个类的设计叫做封装。

封装的语法:
class 类名{
 	访问权限:数据属性;
	访问权限:函数行为;
 }

在面向对象编程中,封装是一种将数据和相关操作封装在一起的机制,以实现数据的隐藏和保护。通过封装,我们可以将数据成员和成员函数组织在类中,并控制对它们的访问权限。

例如设计一个汽车CAR类,它有不同的属性,可以用数据类型来表示,也有不同的行为动作,可以用函数来表示。

class Car {
  private:
    string color;
    string brand;
    int speed;

  public:
    void start();
    void accelerate();
    void stop();
};

(1)例一:设计一个圆类,求圆的周长

//例一:设计一个圆类,求圆的周长
#include <iostream>
#include <string>
#include <cmath>
using namespace std;

#define PI 3.1415926

class Circle {
private://数据成员是私有的
    float Radius;//圆的半径

public://函数成员是公开的
    void setRadius(float radius) {//利用类的成员函数接口来从外部实现对私有成员圆的半径的操作
        Radius = radius;
    }

    float getCircumference() {//提供给外部的接口函数
        return 2 * PI * Radius;
    }

    float getArea() {
        return PI * pow(Radius, 2);//提供给外部的接口函数
    }
};


int main() {
    Circle cir1;//实例化一个圆类的对象cir1
    float r;
    cout << "请输入半径:" << endl;
    cin >> r;
    cir1.setRadius(r);
    float Circumference = cir1.getCircumference();
    float Area = cir1.getArea();
    cout << "Circumference of the circle: " << Circumference << endl;
    cout << "Area of the circle: " << Area << endl;
    system("pause");
    return 0;
}

setRadius()函数用于设置圆的半径,getCircumference()函数用于计算圆的周长,getArea()函数用于计算圆的面积。
在main()函数中,通过用户输入来设置圆的半径,并计算并输出圆的周长和面积。

封装就是对一个类里面的数据属性和函数进行设计定义。对象就是主程序中对这个类的实例化。

二、类的封装怎么感觉跟结构体很像呢??

观察得很对,类和结构体在某种程度上是相似的,它们都可以用来封装数据和函数成员。实际上,结构体可以看作是一种简化的类

在C++中,结构体(struct)是一种用户自定义的数据类型,可以包含不同类型的数据成员,但默认情况下,结构体的成员是公共的(public)。结构体通常用于组织相关的数据,但不包含复杂的行为。人话就是结构体里面只有数据,没有函数

类(class)也是一种用户自定义的数据类型,与结构体类似,可以包含不同类型的数据成员。但与结构体不同的是,类的成员默认是私有的(private),需要通过公共的成员函数来访问和操作。类可以定义更复杂的行为,包括成员函数、继承和多态等特性

虽然类和结构体有一些相似之处,但它们的主要区别在于使用的目的和特性。类更适合用于面向对象编程,封装数据和行为,实现代码的组织和封装。而结构体更适合用于简单的数据组织,不涉及复杂的行为。

下面是一个使用结构体和类的示例:

// 使用结构体
struct Point {
  int x;
  int y;
};

// 使用类
class Point {
  private:
    int x;
    int y;

  public:
    void setX(int newX) {
      x = newX;
    }

    void setY(int newY) {
      y = newY;
    }

    int getX() {
      return x;
    }

    int getY() {
      return y;
    }
};

int main() {
  // 使用结构体
  Point p1;
  p1.x = 10;
  p1.y = 20;

  // 使用类
  Point p2;
  p2.setX(30);
  p2.setY(40);

  return 0;
}

在上面的例子中,我们定义了一个结构体"Point"和一个类"Point",它们都包含了两个整型的数据成员"x"和"y"。使用结构体时,我们可以直接访问和修改成员变量。而使用类时,我们需要通过公共的成员函数来设置和获取成员变量的值。

三、类与对象中的一些常规叫法和术语:

类(Class):类是一种抽象的数据类型,用于定义对象的属性和行为。它是对象的模板或蓝图,描述了对象的结构和行为。

对象(Object):对象是类的实例化,是具体的实体。它具有类定义的属性和行为,并可以通过调用类的方法来操作和访问这些属性和行为。

实例化(Instantiate):实例化是创建类的对象的过程。通过实例化,可以根据类的定义创建一个具体的对象。

成员变量(Member Variable):成员变量是定义在类中的变量,用于存储对象的属性。它们也被称为实例变量,因为每个对象都有自己的一组成员变量。

成员函数(Member Function):成员函数是定义在类中的函数,用于操作和访问对象的属性。它们也被称为方法,可以通过对象来调用。

封装(Encapsulation):封装是一种将数据和操作封装在类中的概念。通过将数据和操作封装在类中,可以实现数据的隐藏和保护,只允许通过类的公共接口来访问和操作数据。

继承(Inheritance):继承是一种通过从现有类派生新类的方式来扩展和重用代码的机制。派生类继承了基类的属性和行为,并可以添加自己的特定功能。

多态(Polymorphism):多态是一种允许使用基类类型来引用派生类对象的特性。通过多态,可以以一种统一的方式处理不同类型的对象,提高代码的灵活性和可扩展性。

构造函数(Constructor):构造函数是一种特殊的成员函数,用于在创建对象时初始化对象的状态。它具有与类相同的名称,并且没有返回类型。

析构函数(Destructor):析构函数是一种特殊的成员函数,用于在对象销毁时清理对象的资源。它具有与类相同的名称,前面加上波浪号(~)作为前缀。

补充:(一)封装中第一个重要的知识:成员的三种访问权限

①封装就好比一个黑盒,对里面成员的权限的设置决定了外界是否能看到和访问里面的成员。

成员的访问权限用于控制类的成员(成员变量和成员函数)在类内部和外部的可见性和访问级别。在C++,有三种常见的成员访问权限:

公有(Public):公有成员在类内部和外部都是可见和可访问的。 
它们可以被类的对象直接访问,也可以被类的成员函数访问。
公有成员在类的外部可以通过对象名或指针/引用来访问。

私有(Private):私有成员只在类内部可见和可访问,对于类的外部是不可见的。
私有成员只能被类的成员函数访问,不能被类的对象直接访问。
私有成员通常用于封装类的实现细节和内部状态。

保护(Protected):保护成员在类内部可见和可访问,对于类的外部是不可见的,但对于派生类是可见和可访问的。
保护成员可以被类的成员函数和派生类的成员函数访问。
保护成员通常用于实现继承和派生类的访问控制。

我的理解:三种不同的权限设置决定了类内部的成员能被什么访问。通过控制成员的访问权限,可以实现封装的概念,隐藏类的内部实现细节,提供对外的接口,同时保护数据的安全性和一致性。这样可以提高代码的可维护性、可扩展性和安全性。

②成员变量的权限一般是私有的。

成员变量的权限一般是私有的。将成员变量声明为私有的意味着它们只能在类的内部访问,对于类的外部是不可见的。私有成员变量通常用于封装类的内部状态和实现细节,以保护数据的安全性和一致性。

通过将成员变量设为私有,可以防止外部代码直接访问和修改类的内部数据,只能通过类的公有成员函数来间接访问和修改。这种间接访问和修改的方式提供了更好的控制和封装,使得类的实现细节对外部代码是隐藏的。

举个例子:

class Circle {
private://数据成员是私有的
    float Radius;//圆的半径

public://函数成员是公开的
    void setRadius(float radius) {//利用类的成员函数接口来从外部实现对私有成员圆的半径的操作
        Radius = radius;
    }

    float getCircumference() {//提供给外部的接口函数
        return 2 * PI * Radius;
    }

    float getArea() {
        return PI * pow(Radius, 2);//提供给外部的接口函数
    }
};

圆的半径是私有变量,外界无法用.点运算符来直接访问,而是要通过公开的函数接口setRadius(float radius)来对半径进行赋值。

③保护和私有权限的区别

保护(Protected):保护成员在类内部可见和可访问,对于类的外部是不可见的,但对于派生类是可见和可访问的。
私有(Private):私有成员只在类内部可见和可访问,对于类的外部是不可见的。

人话就是:保护的成员,类外不能访问,但是儿子可以访问父亲的保护内容。私有的成员,类外不能访问,而且儿子也不能访问父亲的私有内容。

④成员函数一般是公开的。

成员函数一般是公开的(即公有成员函数)是因为它们提供了类的外部代码与类进行交互的接口。公有成员函数可以被类的对象直接调用,也可以被类的外部代码通过对象名或指针/引用来调用。

以下是成员函数一般是公开的几个原因:

封装和抽象:公有成员函数定义了类对外部代码提供的接口,通过这些接口,外部代码可以与类进行交互和操作。公有成员函数隐藏了类的内部实现细节,只暴露了必要的操作和功能,实现了封装和抽象的概念

访问控制:公有成员函数可以访问类的私有成员变量,通过公有成员函数,外部代码可以间接地访问和修改类的私有成员变量。这样可以实现对类的内部数据的控制和保护,确保数据的安全性和一致性。

继承和多态:公有成员函数在派生类中可以被继承和重写,从而实现多态性。派生类可以通过继承和重写公有成员函数来扩展和修改基类的行为,实现特定的功能需求。

可访问性:公有成员函数对于类的外部代码是可见和可访问的,可以在外部代码中直接调用。这样可以方便地使用类的功能和操作,提高代码的可用性和可扩展性。

补充:(二)封装中第二个重要的知识:封装中能定义数组和结构体吗?

在封装的概念中,可以定义数组和结构体作为类的成员变量或成员函数的参数、返回值等。
例子一:定义一个数组

class MyClass {
private:
  int myArray[5]; // 私有数组成员变量

public:
  void setArrayValue(int index, int value) {
    if (index >= 0 && index < 5) {
      myArray[index] = value;
    }
  }

  int getArrayValue(int index) {
    if (index >= 0 && index < 5) {
      return myArray[index];
    }
    return -1; // 返回一个默认值,表示索引无效
  }
};

例子二:定义一个结构体

class Point {
private:
  struct Coordinate {
    int x;
    int y;
  } position; // 私有结构体成员变量

public:
  void setPosition(int x, int y) {
    position.x = x;
    position.y = y;
  }

  int getX() {
    return position.x;
  }

  int getY() {
    return position.y;
  }
};

补充:(三)封装中的第三个重要知识点:通过函数来自由控制私有成员属性的读写权限

比如,我有一个人的类,包含三个私有成员变量:年龄、姓名、身高。要求姓名只读,年龄可读可写,身高只写。
那么对着三个变量,最好都用接口函数来进行读取和赋值操作。

class Person {
private:
  std::string name; // 私有成员变量,姓名
  int age; // 私有成员变量,年龄
  double height; // 私有成员变量,身高

public:
  // 读取姓名
  std::string getName() const {
    return name;
  }

  // 读取年龄
  int getAge() const {
    return age;
  }

  // 写入年龄
  void setAge(int newAge) {
    age = newAge;
  }

  // 写入身高
  void setHeight(double newHeight) {
    height = newHeight;
  }
};

补充:(四)类的封装中,成员的生命周期如何?

①成员的生命周期与实例化的对象的生命周期一致。

在类的封装中,成员的生命周期与对象的生命周期紧密相关。成员变量和成员函数的生命周期取决于对象的创建和销毁。

成员变量的生命周期: 成员变量是类的数据成员,用于存储对象的状态信息。当创建一个类的对象时,会为该对象分配内存空间,并在内存中存储对象的成员变量。成员变量的生命周期与对象的生命周期相同,当对象被销毁时,成员变量也会随之销毁。

成员函数的生命周期: 成员函数是类的行为成员,用于操作对象的数据成员。成员函数的生命周期与对象的生命周期相同,当创建一个类的对象时,成员函数也会被创建,并在对象的生命周期内存在。成员函数可以通过对象来调用,以访问和修改对象的成员变量。

需要注意的是,每个对象都有自己的成员变量和成员函数,它们在内存中是独立存储的。这意味着不同对象的成员变量和成员函数是相互独立的,它们的值和行为可以各自独立地进行访问和修改,而不会相互影响。

②那成员函数里面对成员变量赋值的话,成员变量属于局部变量还是全局变量?

在成员函数中对成员变量赋值时,成员变量仍然属于类的成员变量,而不是局部变量或全局变量。

成员函数可以直接访问和修改类的成员变量,因为成员变量是类的数据成员,用于存储对象的状态信息。在成员函数内部,可以使用成员变量的名称来引用它们,并对其进行赋值操作。

尽管在成员函数内部可以直接使用成员变量的名称,但这并不意味着成员变量变成了局部变量。成员变量仍然是属于类的一部分,它们的作用域是整个类,而不仅限于成员函数内部

需要注意的是,成员函数内部的局部变量和成员变量可以具有相同的名称。在这种情况下,成员函数内部的局部变量会遮蔽成员变量,即优先使用局部变量。如果要在成员函数内部访问成员变量而不是局部变量,可以使用成员访问运算符 this-> 来显式指明。

总结起来,成员函数内部对成员变量的赋值操作仍然是对类的成员变量进行赋值,成员变量仍然属于类的一部分,而不是局部变量或全局变量。

文章来源:https://blog.csdn.net/qq_53092944/article/details/135048019
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。