C++多态、虚函数、纯虚函数、抽象类

news2025/1/10 12:12:10

多态的概念
        通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。
        举个简单的例子:抢红包,我们每个人都只需要点击一下红包,就会抢到金额。有些人能抢到几十元,而有些人只能抢到几元甚至几毛。也正说明了不同的人做相同的事,结果却不同,这就是多态。

        在C++中有两种多态性,一种是静态的多态、一种是动态的多态;

静态的多态:函数重载,看起来调用同一个函数却有不同的行为。静态:原理是编译时实现。

动态的多态:一个父类的引用或指针去调用同一个函数,传递不同的对象,会调用不同的函数。动态:原理是运行时实现。


一、前言

        多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。下面的实例中,基类 Shape 被派生为两个类,如下所示:

#include <iostream> 
using namespace std;

class Shape {
public:
    void area()
    {
        cout << "Parent class area :" << endl;
    }
};
class Rectangle : public Shape {
public:
    void area()
    {
        cout << "Rectangle class area :" << endl;
    }
};
class Triangle : public Shape {
public:
    void area()
    {
        cout << "Triangle class area :" << endl;
    }
};

void func(Shape& p) {
    p.area();
}
// 程序的主函数
int main()
{
    Rectangle Rec;
    // 调用矩形的求面积函数 area
    func(Rec);


    Triangle Tri;
    // 调用三角形的求面积函数 area
    func(Tri);

    return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Parent class area :
Parent class area :

        导致错误输出的原因是,调用函数 area() 被编译器设置为基类中的版本,这就是所谓的静态多态,或静态链接 - 函数调用在程序执行前就准备好了。有时候这也被称为早绑定,因为 area() 函数在程序编译期间就已经设置好了。

        但现在,让我们对程序稍作修改,在 Shape 类中,area() 的声明前放置关键字 virtual,其余不变,如下所示:

#include <iostream> 
using namespace std;

class Shape {
public:
   virtual void area()
    {
        cout << "Parent class area :" << endl;
    }
};
class Rectangle : public Shape {
public:
    void area()
    {
        cout << "Rectangle class area :" << endl;
    }
};
class Triangle : public Shape {
public:
    void area()
    {
        cout << "Triangle class area :" << endl;
    }
};

void func(Shape& p) {
    p.area();
}
// 程序的主函数
int main()
{
    Rectangle Rec;
    // 调用矩形的求面积函数 area
    func(Rec);


    Triangle Tri;
    // 调用三角形的求面积函数 area
    func(Tri);

    return 0;
}

修改后,当编译和执行前面的实例代码时,它会产生以下结果:

Rectangle class area :
Triangle class area :

        此时,编译器看的是指针的内容,而不是它的类型。因此,由于 tri 和 rec 类的对象的地址存储在 *shape 中,所以会调用各自的 area() 函数。

        正如您所看到的,每个子类都有一个函数 area() 的独立实现。这就是多态的一般使用方式。有了多态,您可以有多个不同的类,都带有同一个名称但具有不同实现的函数,函数的参数甚至可以是相同的。

二、多态的定义及实现

1.多态的构成条件 

        在继承中要构成多态还有两个条件:

        (1)必须通过基类的指针或者引用调用虚函数。

        (2)被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。

2.虚函数

        虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。
        我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定

        一旦定义了虚函数,该基类的派生类中同名函数也自动成为了虚函数。也就是说在派生类中有一个和基类同名的函数,只要基类加了virtual修饰,派生类不加virtual修饰也是虚函数。虚函数只能是类中的一个成员函数,不能是静态成员或普通函数。

        注意:我们在继承中为了解决数据冗余和二义性的问题,需要用到虚拟继承,关键字也是virtual,和多态中的virtual是没有关系的。

3.虚函数的重写

        虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。

        通过对虚函数的重写,就能够实现多态:

#include<iostream>
using namespace std;

//买票
class Person
{
public:
	virtual void BuyTicket() { cout << "买票-全价" << endl; }
};

//学生买票
class Student : public Person
{
public:
	virtual void BuyTicket() { cout << "买票-半价" << endl; }
};

//军人买票
class Soldier : public Person
{
public:
	void BuyTicket() { cout << "优先-买票-半价" << endl; }

};

//构成多态,传的哪个类型的对象,调用的就是这个类型的虚函数 --- 跟对象有关
//不构成多态,调用就是P的类型 --- 跟类型有关
void Func(Person& p) //或void Func(Person* p)
{
	p.BuyTicket();   //p->BuyTicket(); 
}

int main()
{
	Person ps;
	Func(ps);   //没有任何身份去买票,一定是全价

	Student st;
	Func(st);   //以学生的身份去买票,是半价

	Soldier so;
	Func(so);   //以军人的身份去买票,是优先并且半价

	return 0;
}

4.虚函数重写的两个例外

(1).协变(基类与派生类虚函数返回值类型不同)

        派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。 

另一种解释:

        C++中的协变(Covariance)指的是派生类的返回类型可以是基类函数的返回类型的子类型。当一个派生类继承了一个基类,并且覆盖(override)了基类中的虚函数时,可以使用协变来改变返回类型。

        具体而言,如果基类函数的返回类型是指针或引用,那么派生类中覆盖该函数时,返回类型可以是基类返回类型所指向或引用的类型的派生类型。

实现协变需满足以下条件:

  • 基类中的函数必须是虚函数(使用 virtual 关键字声明)。
  • 派生类中重写的函数必须具有相同的函数签名(函数名、参数列表和常量性)。
  • 派生类中重写的函数的返回类型必须是基类函数返回类型的子类型。

示例:

引用自:C++协变(covariant)-CSDN博客

        假设有一个基类 Animal 和两个派生类 Dog 和 Cat。Animal 类中有一个虚函数 makeSound(),它返回一个指向 Animal 对象的指针。在派生类 Dog 中,可以重写 makeSound() 函数并返回一个指向 Dog 对象的指针。同样,在派生类 Cat 中也可以重写 makeSound() 函数并返回一个指向 Cat 对象的指针。

#include <iostream>
class Animal {
public:
    virtual Animal* makeSound() {
        std::cout << "Animal makes a sound." << std::endl;
        return this;
    }
};
class Dog : public Animal {
public:
    virtual Dog* makeSound() {
        std::cout << "Dog barks." << std::endl;
        return this;
    }
};
class Cat : public Animal {
public:
    virtual Cat* makeSound() {
        std::cout << "Cat meows." << std::endl;
        return this;
    }
};
int main() {
    Animal* animal;
    Dog dog;
    Cat cat;
    animal = &dog;
    animal->makeSound();  // Output: "Dog barks."

    animal = &cat;
    animal->makeSound();  // Output: "Cat meows."
    return 0;
}

协变与多态的区别: 

        C++中协变和多态是密切相关的。多态(Polymorphism)指的是同一个函数在不同的对象上被调用时,可以表现出不同的行为方式。

        在C++中,通过使用虚函数(virtual function),实现了运行时多态的语法机制。基类中的虚函数可以在派生类中被重写(覆盖),当派生类对象调用该虚函数时,会根据对象的实际类型来确定调用哪个虚函数。

        而协变则指的是派生类可以改变继承自基类函数的返回类型,使得返回类型成为基类返回类型所指向或引用的类型的派生类型。

        通过将协变和多态结合起来,我们可以在派生类中覆盖基类的虚函数,并且返回派生类特有的类型。这就允许我们在多态的情况下,在派生类中使用更具体的返回类型。

 (2).析构函数的重写(基类与派生类析构函数的名字不同) 

        如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor。

        在C++中,析构函数是一种特殊的成员函数,用于在对象销毁时执行清理工作。通常情况下,析构函数会自动由编译器生成,默认执行对象的成员变量和基类的析构函数。

        当需要对派生类进行额外的清理工作或资源释放时,可以通过重写(override)基类的析构函数来实现。

        在派生类中重写析构函数需要遵循以下规则:

  1. 函数名与基类的析构函数完全相同。
  2. 参数列表为空。
  3. 返回类型为空(void)。
  4. 可以添加override关键字(可选),以显式地说明正在重写基类的析构函数。

        以下是一个示例代码:

class Base {
public:
    virtual ~Base() {
        // 基类的析构函数
    }
};

class Derived : public Base {
public:
    ~Derived() override {
        // 派生类的析构函数,重写了基类的析构函数
    }
};

        在上述代码中,基类Base定义了一个虚析构函数,派生类Derived通过重写基类的析构函数,实现了自己的清理逻辑。

        需要注意的是,在继承关系中,如果基类的析构函数是一个虚函数,则派生类中的析构函数也应该声明为虚函数。这样,在使用基类指针或引用指向派生类对象,并通过该指针或引用调用析构函数时,能够正确地调用到派生类的析构函数。

        总之,通过在派生类中重写基类的析构函数,可以实现额外的清理工作或资源释放。重写析构函数需要遵循特定的规则,并且建议将基类的析构函数声明为虚函数。

 

另一种解释:

(引用自:C++ 多态(一) : 多态的构成条件、final、override、协变、析构函数的重写、抽象类_c++ 多态 override-CSDN博客)

        析构函数虽然函数名不同,但是也能构成重写,因为站在编译器的视角,他所调用的析构函数名称都叫做destructor。

为什么编译器要通过这种方式让析构函数也能构成重写呢?

假设我用一个基类指针或者引用指向派生类对象,如果不构成多态会怎样?

class Human
{
public:
	~Human()
	{
		cout << "~Human()" << endl;
	}
};

class Student : public Human
{
public:
	~Student()
	{
		cout << "~Student()" << endl;
	}
};

int main()
{
	Human* h = new Student;
	delete h;

	return 0;
}

输出结果: 

~Human()

分析:

        上述代码只会调用类Human的析构函数,即如果不构成多态,那么指针是什么类型的,就会调用什么类型的析构函数,这也就导致了一种情况,如果派生类的析构函数中有资源释放,而这里却没有释放掉那些资源,就会导致内存泄漏的问题。

        所以为了防止这种情况,必须要将析构函数定义为虚函数。这也就是编译器将析构函数重命名为destructor的原因。

class Human
{
public:
	virtual ~Human()
	{
		cout << "~Human()" << endl;
	}
};

class Student : public Human
{
public:
	virtual ~Student() // 该virtual关键字可省略
	{
		cout << "~Student()" << endl;
	}
};

int main()
{
	Human* h = new Student;
	delete h;

	return 0;
}

输出结果: 

~Student()
~Human() 

5.C++11 override和final

        从上面可以看出,C++对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数名字母次序写反而无法构成重载,而这种错误在编译期间是不会报出的,只有在程序运行时没有得到预期结果才来debug会得不偿失,因此:C++11提供了 override 和 final 两个关键字,可以帮助用户检测是否重写。

(1) final

        在C++11标准中,final是一个关键字,用于禁止继承和覆盖类的虚函数。当一个类或者一个类的成员函数被声明为final时,意味着它不能再被其他类继承或者它的虚函数不能被派生类覆盖。

使用final关键字的好处是:

  1. 可以增强代码的安全性:使用final关键字可以防止不恰当的继承和覆盖。
  2. 可读性:使用final关键字可以增强代码的可读性和可维护性,明确了类或函数的意图。

final:修饰虚函数,表示该虚函数不能再被重写。


#include<iostream>
class Car
{
public:
	virtual void Drive() final
	{}
};

class Benz :public Car
{
public:
	virtual void Drive() override //检查是否完成重写
	{
		std::cout << "Benz-舒适" << std::endl;
	}
};
int main() {
	Benz benz;
	benz.Drive(); 
}

 上述程序因为final关键字的存在会报错,报错原因是:

final:修饰类,表示该类不能再被继承。

示例:


#include<iostream>
class Car final
{
public:
	virtual void Drive() 
	{}
};

class Benz :public Car 
{
public:
	virtual void Drive()  //检查是否完成重写
	{
		std::cout << "Benz-舒适" << std::endl;
	}
};
int main() {
	Benz benz;
	benz.Drive(); 
}

上述程序报错: 

不能将final用于基类,否则程序报错! 

(2) override

        在C++中,override是一个特殊的关键字,用于显式地标识派生类中的函数是覆盖(override)基类中的虚函数。

        当派生类中的函数与基类中的虚函数具有相同的名称、参数列表和返回类型时,可以使用override关键字来明确指示该函数是对基类函数的覆盖。

使用override关键字的好处是:

  1. 错误检查:编译器会在编译时检查是否存在函数覆盖错误。如果派生类中使用了override关键字,但没有正确地覆盖基类中的虚函数,编译器将报错。
  2. 可读性:使用override关键字可以增强代码的可读性和可维护性,明确了派生类函数的意图。

下面是一个示例代码:


#include<iostream>
class Car
{
public:
	virtual void Drive()
	{}
};

class Benz :public Car
{
public:
	virtual void Drive() override //检查是否完成重写
	{
		std::cout << "Benz-舒适" << std::endl;
	}
};
int main() {
	Benz benz;
	benz.Drive(); // Benz-舒适
}

三、抽象类

纯虚函数

        您可能想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。

        在虚函数的后面写上 = 0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。

class Shape {
   public:
      // pure virtual function
      virtual int area() = 0;
};

   = 0 告诉编译器,函数没有主体,上面的虚函数是纯虚函数

包括纯虚函数的类叫做抽象类,也叫接口类,抽象类不能实例化出对象。

示例:

#include<iostream>
//抽象类
class Car
{
public:
	virtual void Drive() = 0;//纯虚函数 
};

int main()
{
	Car c;//抽象类不能实例化出对象
	return 0;
}

上述程序运行报错: 

 派生类继承后也不能实例化出对象。

示例:

#include<iostream>
class Car
{
public:
	virtual void Drive() = 0; // 纯虚函数
};
class Benz :public Car{};
int main()
{
	Benz b1;
}

上述程序运行出错: 

        派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。

示例:

#include<iostream>
class Car
{
public:
	//纯虚函数一般只声明,不实现(可以实现,但没有价值,因为不能实例化出对象,可以定义指针或引用)
	virtual void Drive() = 0;
};

class Benz :public Car
{
public:
	virtual void Drive()
	{
		std::cout << "Benz-舒适" << std::endl;
	}
};

class BMW :public Car
{
public:
	virtual void Drive()
	{
		std::cout << "BMW-操控" << std::endl;
	}
};

int main()
{
	//派生类只有重写了纯虚函数才能实例化出对象
	Benz b1;
	BMW b2;
	//通过基类的指针去调用不同对象的函数
	Car* pBenz = new Benz;
	pBenz->Drive();
	Car* pBMW = new BMW;
	pBMW->Drive();
}

输出结果: 

Benz-舒适
BMW-操控

接口继承和实现继承

  • 普通函数的继承是一种实现继承,派生类继承了基类的普通成员函数,可以使用函数,继承的是函数的实现。
  • 虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。
  • 所以如果不实现多态,不要把函数定义成虚函数。

参考自(很值得学习):

【精选】【C++】—— 多态_c++多态_霄沫凡的博客-CSDN博客

注:参考内容只是为了自身学习,并无其他想法!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1118437.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Windows11突然VM虚拟机无法运行报错与 Device/Credential Guard 不兼容

windows11长时间没用vmware,突然使用时打开报一下错&#xff1a; 解决方案&#xff1a; 以管理员身份运行“Windows Powershell (管理员)”&#xff08;Windows键X键&#xff09;&#xff0c;输入以下命令重启电脑。 bcdedit /set hypervisorlaunchtype off 注意&#xff1a…

有哪些专业的配音APP推荐?

作为当今社交媒体时代的一员&#xff0c;我们经常需要在各种场合中使用配音软件&#xff0c;无论是自制视频内容还是进行个人创作&#xff0c;一款好用且免费的配音软件显得很重要。今天给大家分享一款备受好评的免费配音软件&#xff0c;它不仅功能强大&#xff0c;而且操作简…

2023年【山东省安全员A证】考试内容及山东省安全员A证考试报名

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 山东省安全员A证考试内容是安全生产模拟考试一点通生成的&#xff0c;山东省安全员A证证模拟考试题库是根据山东省安全员A证最新版教材汇编出山东省安全员A证仿真模拟考试。2023年【山东省安全员A证】考试内容及山东省…

MS17010(永恒之蓝)漏洞实战

曾因苦难多壮志&#xff0c;不教红尘惑坚心。 工具检测 实战过程 使用搜索命令&#xff0c;搜索ms17_010 search ms17_010 搜索网段中主机漏洞 use auxiliary/scanner/smb/smb_ms17_010 照例&#xff0c;show options 看一下配置 设置网段&#xff0c;run运行就行了 使用攻…

leetcode 310 最小高度树

树是一个无向图&#xff0c;其中任何两个顶点只通过一条路径连接。 换句话说&#xff0c;一个任何没有简单环路的连通图都是一棵树。 给你一棵包含 n 个节点的树&#xff0c;标记为 0 到 n - 1 。给定数字 n 和一个有 n - 1 条无向边的 edges 列表&#xff08;每一个边都是一对…

代码随想录算法训练营第二十九天 | 回溯算法总结

​ 代码随想录算法训练营第二十九天 | 回溯算法总结 1. 组合问题 1.1 组合问题 在77. 组合中&#xff0c;我们开始用回溯法解决第一道题目&#xff1a;组合问题。 回溯算法跟k层for循环同样是暴力解法&#xff0c;为什么用回溯呢&#xff1f;回溯法的魅力&#xff0c;用递…

excel常用的几个函数

1、MID函数 通常用来返回返回指定字符串中的子串。 函数公式&#xff1a; MID(string, starting_at, extract_length) String&#xff08;必填&#xff09;&#xff1a;包含要提取字符的文本字符串 starting_at&#xff08;必填&#xff09;&#xff1a;文本中要提取的第一个字…

Tomcat及jdk安装下载及环境配置(超超超详解)

我是看了两篇博客安装配置好的 jdk 最详细jdk安装以及配置环境&#xff08;保姆级教程&#xff09;_安装jdk需要配置环境变量吗-CSDN博客 tomcat Tomcat的下载安装与配置及常见问题处理【Win11】 - 鞠雨童 - 博客园 (cnblogs.com) 本篇文章是我解决了很多朋友的tomcat配置问题总…

基于RIP的MGRE实验

题目及视图&#xff1a; 实验要求&#xff1a; 1.R5为ISP&#xff0c;只能进行IP地址配置&#xff0c;其所有地址均配为公有IP地址 2.R1和R5间使用PPP的PAP认证&#xff0c;R5为主认证 R2与R5之间使用ppp的CHAP认证&#xff0c;R5为主认证方 R3与R5之间使用HDLC封装 3.R1…

【C++学习笔记】引用

1. 概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内存空 间&#xff0c;它和它引用的变量共用同一块内存空间。 比如&#xff1a;孙悟空&#xff0c;有人叫弼马温&#xff0c;也有人称为齐天大圣。 1.1 使用方…

计算机毕业设计 基于SpringBoot智慧养老中心管理系统的设计与实现 Javaweb项目 Java实战项目 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

粤嵌实训医疗项目--day01(Vue+SpringBoot)

目录 一、创建工作空间及配置Maven环境 二、创建springboot项目整合web操作 三、http请求参数获取及登录页面访问操作 四、数据库设计、数据库创建及导入sql 五、使用mybatis-plus逆向工程生成代码【vaccinum】 六、JavaEE三层架构概念及user查询实现 七、mybatis-plus逆…

安装与脏数据绕过_安全狗

1安全狗 1.1 环境准备 安全狗safedogwzApacheV3.5.exe&#xff0c;安装步骤省略&#xff0c; pikachu环境&#xff1a;https://zhuanlan.zhihu.com/p/568493971 安装注意事项&#xff1a;安装完后php和web服务都需要重启 注意事项&#xff1a;服务名php版本保持一致 安装过…

UG\NX二次开发 获取调色板CDF文件的内容

文章作者:里海 来源网站:《里海NX二次开发3000例专栏》 感谢粉丝订阅 感谢 迈克尔.卓别煷 订阅本专栏,非常感谢。 简介 UG\NX二次开发 获取调色板CDF文件的内容 文件->首选项->调色板 效果 代码 #include "me.hpp" #

使用Simple JWT提供认证服务(详细介绍access_token和refresh_token的使用)

文章目录 基本概念JSON Web Token&#xff08;JWT&#xff09;Simple JWT 主要用途Cookie、Session、Token的区别CookieSessionToken Token续签access_token 和 refresh_token时效设置 基本概念 JSON Web Token&#xff08;JWT&#xff09; JSON Web Token&#xff08;JWT&am…

探索大数据时代的关键技术:数据挖掘、可视化和数据仓库

文章目录 &#x1f31f; 大数据和数据分析技术&#x1f34a; 引言&#x1f34a; 数据挖掘&#x1f34a; 数据可视化&#x1f34a; 数据仓库&#x1f34a; 结论 &#x1f4d5;我是廖志伟&#xff0c;一名Java开发工程师、Java领域优质创作者、CSDN博客专家、51CTO专家博主、阿里…

【Linux基础】详谈Shell运行原理------王婆传媒(高重复率面试题)

目录 &#x1f4a7;前言 &#x1f4a6;Shell的运行原理 &#x1f449;Shell的基本概念与作用 &#x1f449;原理的展示与剖析 &#x1f449;Shell外壳感性理解【一门亲事】 &#x1f4a7;总结 &#x1f4a7;共勉 &#x1f4a7;前言 在之前的 Linux 讲解中&#xff0c;主要说…

【管理运筹学】第 10 章 | 排队论(2,到达时间间隔的分布和服务时间的分布)

文章目录 引言一、普阿松分布二、负指数分布三、爱尔朗分布 引言 解决排队问题&#xff0c;首先要根据原始资料做出顾客到达时间间隔和服务时间的经验分布&#xff0c;然后按照统计学的方法&#xff08;如 χ 2 \chi^2 χ2 检验&#xff09;以确定属于哪种分布理论&#xff0…

JS数组方法合集(含应用场景)

1.Array.push() 向数组的末尾添加一个或多个元素&#xff0c;并返回新的数组长度。原数组改变 const arr ["apple", "orange", "grape"]; const arr_length arr.push("banana");console.log("arr", arr, "arr_leng…