【C++】——多态(上)

【C++】——多态(上)

  • 1 多态的概念
  • 2 多态的定义及实现
    • 2.1 多态的构成条件
      • 2.1.1 实现多态的必要条件
      • 2.1.2 虚函数
      • 2.1.3 感受多态
      • 2.1.4 判断是否满足多态
      • 2.1.5 多态场景的一道选择题
      • 2.1.6 虚函数重写的一些其他问题
        • 2.1.6.1 协变
        • 2.1.6.2 析构函数的重写
      • 2.1.7 override 和 final 关键字
      • 2.1.8 重载/重写/隐藏的对比重载
  • 3 纯虚函数和抽象类

1 多态的概念

  多态的概念:通俗来说,多态就是多种形态。多态分为静态多态(编译时的多态)动态多态(运行时多态)。本章我们重点介绍运行时多态

  编译时多态(静态多态) 主要就是我们前面学习的函数重载函数模板,他们传不同类型的参数就可以调用不同的函数,通过参数不同达到多种形态。之所以叫编译时多态,是因为他们实参传给形参的参数匹配是在编译时完成的,我们把编译时一般归为静态,运行时归为动态
  
比如:

int i;
double d;
cout << i;
cout << d;

  上面,好像我们调用 cout 好像是同一个函数,但实际上真的是同一个函数吗?
  并不是,这里运用了函数重载。在编译时,编译器通过一些规则去进行对应的匹配,调用对应的流插入,是 i n t int int 就调用 i n t int int 的流插入; d o u b l e double double 就调用 d o u b l e double double 的流插入。这里对一个函数名实现出多重形态,实际上底层还是去调用不同的函数
  函数模板也是类似的道理。
  
  我们今天主要学习运行时多态(动态多态):当我们具体完成某个行为时,不同的对象去做这个行为时,结果是不一样的

比如:
  现实中的买票行为。普通人买票时,买的是全票;学生买票时,买的是优惠票;军人买票时,可以优先买票。
再比如:
  同样是动物叫的一个行为(函数),传猫过去是喵喵喵,传狗过去是汪汪汪。
  
  

2 多态的定义及实现

2.1 多态的构成条件

  多态必须是在继承的条件下面,去调用同一个函数(返回值,函数名,参数都相同),产生了不同的行为
  

2.1.1 实现多态的必要条件

实现多态必须要满足以下两个条件

  • 必须是指针引用调用虚函数
  • 被调用的函数必须是虚函数,派⽣类必须完成对基类的虚函数重写/覆盖

  说明:要实现多态的效果:第一必须是基类指针引用,因为只有基类的指针或引用才能既指向基类又指向派生类对象;第二派生类必须对基类的虚函数进行重写/覆盖,如此派生类才能有不同的函数,多态的不同形态效果才能达到。

  多态的现本质就是调用虚函数
  那什么又是虚函数呢?我们一起来看看
  

2.1.2 虚函数

  类成员函数前面加 v i r t u a l virtual virtual(返回值前面),那么这个成员函数被称为 虚函数
非成员函数不能加 virtual 修饰
:在继承章节,我们曾讲过虚继承也是用 v i r t u a l virtual virtual 关键字,但这里的 v i r t u a l virtual virtual 和虚继承中的 v i r t u a l virtual virtual 功能上没有任何关系。这里属于一鱼两吃。

  那么说明中虚函数的重写覆盖又是什么意思呢?我们一起来看看

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

我们再来理清楚各个条件之间的关系:
  多态有两个条件必须是指针引用调用虚函数调用虚函数必须完成重写/覆盖
  而虚函数重新/覆盖又要满足:必须是基类和派生类中的两个虚函数,两个虚函数必须完全相同(只有函数体可以不同)

  
  

2.1.3 感受多态

class Person 
{public:    virtual void BuyTicket() { cout << "买票-全价" << endl; }
};class Student : public Person 
{
public:    virtual void BuyTicket() { cout << "买票-打折" << endl;}};void Func(Person* ptr)
{// 这⾥可以看到虽然都是Person指针Ptr在调用BuyTicket// 但是跟ptr没关系,⽽是由ptr指向的对象决定的。ptr->BuyTicket();
}int main()
{Person ps;Student st;Func(&ps);Func(&st);return 0;
}

  为什么void Func(Person* ptr)的参数要基类的指针或引用呢?在继承章节,我们曾讲过切片,只有参数是基类,才能既可以传父类又可以传子类对象(详情请看:【C++】—— 继承(上))

  这时就达到了一个效果:指向谁调用谁。传递 p s ps ps,指向的是基类,就调用基类的虚函数;传递 s t st st,指向派生类,调用派生类的虚函数。传递不同的对象,调用不同的函数

运行结果:

在这里插入图片描述

  

  如果没有学习多态,实际调用的类型是根据表达式的类型来确定的 f u n c func func 函数参数类型是 P e r s o n Person Person*,因此不论传递父类还是子类指针,传递给形参 p t r ptr ptr 都会强转成 P e r s o n Person Person*,最终调用的都是父类的函数。
  现在有了多态,实际调用的函数实在运行时根据对象的实际类型来确定的。指向谁调用谁,指向父类调父类,指向子类调子类我们传递不同的对象,实现调用不同的函数。
  
  当然,引用也是可以的

class Animal
{
public:virtual void talk() const{}
};
class Dog : public Animal
{
public:virtual void talk() const{std::cout << "汪汪" << std::endl;}
};class Cat : public Animal
{
public:virtual void talk() const{std::cout << "(>^ω^<)喵" << std::endl;}
};
void letsHear(const Animal& animal)
{animal.talk();
}
int main()
{Cat cat;Dog dog;letsHear(cat);letsHear(dog);return 0;
}

运行结果:

在这里插入图片描述

  

2.1.4 判断是否满足多态

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

  不满足第二个条件,基类BuyTicket()不是虚函数

运行结果:
在这里插入图片描述
  


  

class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-打折" << endl; }
};void Func(Person ptr)
{ptr->BuyTicket()
}

  不满足第一个条件,不是基类的指针或应用调用

运行结果:
在这里插入图片描述

  
  不满足多态编译器看调用的参数类型void Func(Person ptr),类型父类是父类,类型子类是子类
  满足多态则看起指向的对象
  


  
这种情况下满足多态吗

class Animal
{
public:virtual void talk() const{}
};
class Dog : public Animal
{
public:virtual void talk() const{std::cout << "汪汪" << std::endl;}
};class Cat : public Animal
{
public:virtual void talk() const{std::cout << "(>^ω^<)喵" << std::endl;}
};int main()
{Cat* pcat = new Cat;Dog* pdog = new Dog;pcat->talk();pdog->talk();return 0;
}

  不满足,因为多态要求是基类的指针/引用来调用,这里 p c a t pcat pcat p d o g pdog pdog 都是派生类的指针来调用。但基类和派生类是相对的,如果有 A A A B B B 类继承了 p c a t pcat pcat p d o g pdog pdog,那么他们同时还是基类,这样调用就构成多态了。
  


  
  那可不可以是子类的指针或引用呢

class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-打折" << endl; }
};
void Func(Student* ptr)
{ptr->BuyTicket();
}

  也是不行的,必须是父类的指针或调用。因为如果是子类的,那就不能传递父类的对象,也就没有多态的概念了
  


  
  那这样满足多态的条件吗

class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:void BuyTicket() { cout << "买票-打折" << endl; }
};
void Func(Person* ptr)
{// 这⾥可以看到虽然都是Person指针Ptr在调⽤BuyTicket// 但是跟ptr没关系,⽽是由ptr指向的对象决定的。ptr->BuyTicket();
}

  是满足的

注意:在重写基类虚函数时,派生类的虚函数在不加 v i r t u a l virtual virtual 关键字时,虽然也可以构成重写(因为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该钟写法不是很规范,不建议这样使用,不过在考试选择题中,经常会故意挖这个坑,让你判断是否构成多态。
  

2.1.5 多态场景的一道选择题

  • 以下程序输出结果是什么()
    A : A A: A A:A -> 0    B : B B: B B:B -> 1    C : A C: A C:A -> 1   D : B D: B D:B ->0    E E E: 编译出错    F F F: 以上都不正确
class A
{
public:virtual void func(int val = 1) { std::cout << "A->" << val << std::endl; }virtual void test() { func(); }
};
class B : public A
{
public:void func(int val = 0) { std::cout << "B->" << val << std::endl; }
};
int main(int argc, char* argv[])
{B* p = new B;p->test();return 0;
}
  • 首先 f u n c func func函数是虚函数,并完成了重写

  • 接着 n e w new new 了一个子类对象 B B B,并通过 p p p 来指向 B B B

  • 再接着, p p p 调用 子类 B B B 从父类 A A A 继承过来的 t e s t ( ) test() test() 函数,这是个普通调用

  • t e s t ( ) test() test() 函数中去调用 f u n c ( ) func() func() 函数。这里 t e s t ( ) test() test() 中的 this指针 A A A* 还是 B B B* 呢;如果是 A A A*, f u n c func func函数是通过父类的指针调用,满足多态的条件,如果是 B B B* 不满足多态的条件

  • 这里 t e s t test test函数中的 t h i s this this A A A*,为什么呢? B B B 不是继承了 A A A 吗,现在是 B B B 调用 t e s t test test,不应该是 B B B*吗?其实继承是一个形象的说法,继承的意思是我可以用,不会将函数中的参数给改了。编译器不会真的将 A A A 中的成员变量和不是重写成员函数都拷贝一份到 B B B 中。

  • 因此,是满足多态的条件的,调用 B B B 类中的 f u n c func func 函数

  • 那答案是 B->0 了吗?还没完

  • 虚函数重写,只重写函数体的实现,不重写声明! 也就是说继承而来的缺省值是不会重写的,可以认为重写后的虚函数 = 基类的函数声明 + 派生类的函数体。也就是说 B B B 类中的虚函数本质是:virtual void func(int val = 0) { std::cout << "A->" << val << std::endl; }

  • 所以正确答案是 B->1

  
  那如果是这样调用,答案又选什么呢

int main(int argc, char* argv[])
{B* p = new B;p->func();return 0;
}

   这里是子类 B B B 去调用,不满足多态的条件,因此 B B B 中的 f u n c func func函数 也不用去组合。这里是普通调用,直接调用 B B B 类原本的 f u n c func func 函数。答案: B->0

  

2.1.6 虚函数重写的一些其他问题

2.1.6.1 协变

  上述说到完成虚函数的重新必须满足三同(返回参数、函数名、参数类型),但是也有例外,那就是协变的情况。

协变的概念

  • 派生类重写基类虚函数时,可以与基类虚函数返回值类型不同。但要求基类虚函数返回基类对象的指针或引用派生类虚函数返回派生类对象的指针或引用,称为协变

  :返回的基类和派生类可以不是自己本身,但他们必须是一对基类和派生类
  协变的实际意义并不大,我们了解一下即可

class A {};
class B : public A {};
class Person {
public:virtual A* BuyTicket(){cout << "买票-全价" << endl;return nullptr;}
};
class Student : public Person {
public:virtual B* BuyTicket(){cout << "买票-打折" << endl;return nullptr;}
};
void Func(Person* ptr)
{ptr->BuyTicket();
}
int main()
{Person ps;Student st;Func(&ps);Func(&st);return 0;
}

运行结果:

在这里插入图片描述

  
  

2.1.6.2 析构函数的重写

  当基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加 v i r t u a l virtual virtual 关键字,都与基类的析构函数构成重写
  虽然基类与派生类析构函数名字看起来不符合重写的规则,实际上编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成 d e s t r u c t o r ( ) destructor() destructor(),所以基类的析构函数加了 v i r t u a l virtual virtual 修饰,派生类的析构函数就构成重写

class A
{
public :virtual ~A(){cout << "~A()" << endl;}
};
class B : public A {
public:~B(){cout << "~B()->delete:" << _p << endl;delete _p;}
protected:int* _p = new int[10];
};

  传统的意义上,哪怕 ~B() 加上 v i r t u a l virtual virtual 也不会构成重写(不符合三同中的函数名相同),但经过编译器将析构函数函数名处理成 d e s t r u c t o r ( ) destructor() destructor() 后,再加上 v i r t u a l virtual virtual 就构成了重写了。
  但 C++ 为什么设计成这样呢?
  
  我们实践中会遇到这样的问题

int main()
{A* p1 = new A;A* p2 = new B;delete p1;delete p2;return 0
}

  现在我有一个父类对象的指针,他可以指向父类对象也可以指向子类对象。当我们进行 d e l e t e delete delete 时,delete p1;没问题,但delete p2;是调用父类对象的析构还是子类对象的析构呢?

  我们期望的是delete p2;调用的是子类的析构函数,但是正常来说(不构成多态情况下)delete p2;是调用父类的析构函数,因为 p 2 p2 p2 A A A* 类型。

  这种情况只有构成多态才能解决问题。构成多态,指针调用时不是跟 p1 和 p2 的类型有关,而是跟他们指向的对象有关,指向父类调父类;指向子类调子类。

  编译器将析构函数统一处理为 d e s t r u c t destruct destruct,就是为了能够实现析构函数的重写,这样只要在父类析构函数前加 v i r t u a l virtual virtual 就构成多态。只有这样才能正确释放资源

运行结果:

在这里插入图片描述

  为什么最后会多调用一次父类的?
  这一点我们在【C++】—— 继承(上)中提到过:这是为了保证析构的顺序是先子后父
  
不构成多态的情况:

class A
{
public :~A(){cout << "~A()" << endl;}
};
class B : public A {
public:~B(){cout << "~B()->delete:" << _p << endl;delete _p;}
protected:int* _p = new int[10];
};
int main()
{A* p1 = new A;A* p2 = new B;delete p1;delete p2;return 0;
}

运行结果:

在这里插入图片描述

   p 1 p1 p1 p 2 p2 p2 都是调用父类的析构函数 p 2 p2 p2 无法调到子类的析构函数

  
  

2.1.7 override 和 final 关键字

  从上面可以看出,C++ 对函数重写的要求比较严格,但是有些情况下由于疏忽,比如函数名写错参数写错等导致无法构成重写,而这种错误在编译期间时不会报出的,只有在程序运行时没有得到预期结果才来 d e b u g debug debug 会得不偿失,因此 C++11 提供了 o v e r r i d e override override关键字可以帮助用户检测是否重写,如果没有完成重写,在编译时会报错

class Car {
public:virtual void Dirve() {}
};
class Benz :public Car {
public:virtual void Drive() override{ cout << "Benz-舒适" << endl; }
};
int main()
{return 0;
}

在这里插入图片描述

  函数名不同( D i r v e Dirve Dirve D r i v e Drive Drive),没有构成重写,编译报错。不加 o v e r r i d e override override,编译时是检查不出来的。
  


  
  如果我们不想让派生类重写这个虚函数,那么可以用 f i n a l final final 去修饰
  在继承章节中我们曾提过:如果一个类我们不想让他被继承,我们可以用 f i n a l final final 去修饰他;被 final 修饰了他就是最终类。
  多态这里是不能被重写:如果一个虚函数不想被重写 就可以用 f i n a l final final 去修饰

class Car {
public:virtual void Dirve() final{}
};
class Benz :public Car {
public:virtual void Dirve() { cout << "Benz-舒适" << endl; }
};
int main()
{return 0;
}

在这里插入图片描述

  同时,既然析构函数函数名都被编译器处理成 d e s t r u c t o r destructor destructor,那也意味着他们构成隐藏,如果想要显式调用父类的析构函数需指定类域。详情请看:【C++】—— 继承(上)

  

2.1.8 重载/重写/隐藏的对比重载

重载

  • 两个函数在同一作用域
  • 函数名相同,但参数类型或个数不同,返回类型可同、可不同

重写/覆盖

  • 两个函数分别在继承体系的父类和子类不同作用域
  • 函数名、参数、返回值都必须相同。协变除外
  • 两个函数都必须是虚函数

隐藏

  • 两个函数分别在继承体系的父类和子类不同作用域
  • 函数名相同
  • 两函数只要不构成重写就是隐藏
  • 父子类的成员变量相同也叫隐藏

重载/重写/隐藏都有一个共同点:函数名相同
  
  

3 纯虚函数和抽象类

  在虚函数的后面写上 =0,则这个函数为纯虚函数,纯虚函数不需要定义实现(实现没啥意义,因为要被派生类重写,但是语法上可以实现),只要声明即可。
  包含纯虚函数的类叫做抽象类抽象类不能实例化出对象,如果派生类继承后不重写纯虚函数,那么派生类也是抽象类
  纯虚函数某种程度上强制了派生类重写虚函数,因为不重写实例化不出对象。

:纯虚函数不需要定义实现,但是语法上可以实现的,选择题爱考

  什么时候我们可以定义抽象类呢。比如现实世界中定义了某个类,这个类不够具体或现实中不存在这个类,也不需要将这个类实例化出对象,这时就可以用抽象类。

  很多时候,我们将基类定义为抽象类。比如动物的叫声:我们将基类 a n i m a l animal animal 定义成抽象类,因为动物是一个统称,不存在具体的叫声;我们可以在子类 C a t Cat Cat D o g Dog Dog 中进行重写, C a t Cat Cat D o g Dog Dog 是一具体的类,有其叫声,可以实例化出来

class Animal
{
public:virtual void talk() const = 0;
};class Cat : public Animal
{
public:virtual void talk() const{std::cout << "(>^ω^<)喵" << std::endl;}
};
class Dog : public Animal
{
public:virtual void talk() const{std::cout << "汪汪" << std::endl;}
};
int main()
{Animal* pCat = new Cat;Animal* pDog = new Dog;pCat->talk();pDog->talk();return 0;
}

运行结果:

在这里插入图片描述

  虽然父类 A n i m a l Animal Animal 不能实例化出对象,但是父类的指针或引用是可以定义的,依然可以实现多态。

int main()
{Animal animal;return 0;
}

在这里插入图片描述

  

class Car
{
public :virtual void Drive() = 0;
};class Benz :public Car
{};

问: B e n z Benz Benz类是抽象类吗?
  是的, B e n z Benz Benz 继承了 C a r Car Car,但是并没有重写虚函数Drive()。虽然 B e n z Benz Benz 不直接包含纯虚函数,但它继承了 C a r Car Car,将其纯虚函数继承了下来,因此 B e n z Benz Benz 也有纯虚函数

class Car
{
public :virtual void Drive() = 0;
};
class Benz :public Car
{
public :virtual void Drive(){cout << "Benz-舒适" << endl;}
};
class BMW :public Car
{
public :virtual void Drive(){cout << "BMW-操控" << endl;}
};

  所以纯虚函数某种程度上强制了子类必须重写虚函数

  
  
  
  


  好啦,本期关于 多态 的知识就介绍到这里啦,希望本期博客能对你有所帮助。同时,如果有错误的地方请多多指正,让我们在 C++ 的学习路上一起进步!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度学习-26-基于PyTorch的多层感知机DNN

文章目录 1 代码分析1.1 加载数据集1.2 定义模型1.3 定义损失函数和优化器1.4 定义训练函数1.4.1 定义累加器Accumulator1.4.2 计算准确率accuracy1.4.3 评估函数evaluate_accuracy1.4.4 单轮训练函数train_epoch1.4.5 训练函数train1.2 执行训练2 整体代码3 参考附录1 代码分析…

使用Python构建一个高级计算器

使用Python构建一个高级计算器 在现代编程中&#xff0c;构建一个功能强大的计算器不仅是学习编程的好项目&#xff0c;还有助于提高对数据处理和用户交互的理解。本文将带您通过使用Python构建一个高级计算器&#xff0c;该计算器支持基本运算、科学运算以及简单的图形用户界…

将SpringBoot项目部署到linux服务器使得本地可以访问

首先我们要先从本地打包jar文件上传到linux中&#xff0c;这些的步骤可以参考其他打包上传的博客哈 打包上传后&#xff0c;可以看到对应的 .jar 文件 如果这样直接运行java -jar code-sandbox-0.0.1-SNAPSHOT.jar 是不行的&#xff0c;因为你还没有在服务器上开放端口&#x…

ubuntu中使用cmake编译报错No CMAKE_CXX_COMPILER could be found.的解决方法

ubuntu中使用cmake编译报错No CMAKE_CXX_COMPILER could be found.的解决方法 No CMAKE_CXX_COMPILER could be found.Could NOT find CUDA (missing: CUDA_NVCC_EXECUTABLE CUDA_CUDART_LIBRARY)Could not find a package configuration file provided by "OpenCV" …

中国制造业精益生产管理的现状与挑战

在当今全球制造业竞争日益激烈的背景下&#xff0c;精益生产管理作为一种高效、灵活的生产模式&#xff0c;已成为众多企业追求的核心竞争力之一。然而&#xff0c;尽管精益生产理念在中国制造业中已得到广泛传播和应用&#xff0c;其实践水平却参差不齐&#xff0c;多数企业仍…

Ansible概述

目录 一、ansible简介 二、absible的特点 三、ansible的工作原理以及流程 四、ansible环境安装部署 五、ansible命令行模块 六、inventory 主机清单 一、ansible简介 Ansible是一个基于Python开发的配置管理和应用部署工具&#xff0c;现在也在自动化管理领域大放异彩。…

解决ultralytics中的YOLOv8在执行task.py文件添加模块操作出现的KeyError报错

报错详情&#xff1a; 在ultralytics项目文件夹内运行/home/xxx/ultralytics/train.py进行单GPU训练的时候训练可以正常进行 from ultralytics import YOLO# Load a model model YOLO("/home/xxx/ultralytics/ultralytics/cfg/models/v8/yolov8s-FASFF.yaml") # …

3.C++经典实例-计算一个数的阶乘

阶乘&#xff08;factorial&#xff09;是‌基斯顿卡曼于1808年发明的运算符号&#xff0c;用于表示一个正整数n的所有小于及等于该数的正整数的积。自然数n的阶乘写作n!。例如&#xff0c;5的阶乘表示为5! 1 2 3 4 5 120。 阶乘在数学和计算机科学中有广泛的应用。例如…

git--git reset

HEAD 单独一个HEAD eg:git diff HEAD 表示当前结点。 HEAD~ HEAD~只处理当前分支。 注意&#xff1a;master分支的上一个结点是tmp分支的所在的结点fc11b74, 79f109e才是master的第二个父节点。 HEAD~ 当前结点的父节点。 HEAD~1 当前结点的父节点。 HEAD~n 当前结点索…

react+video:限制快进、倍速、画中画

实现代码&#xff1a; <video ref{videoRef} src{videoUrl} className{style.video} controls onRateChange{rateChange} onPlay{playVideo} onPause{pauseVideo} onTimeUpdate{timeUpdate} disablePictureInPicture playsInline poster{poster} controlsList"nodownl…

Qml-Item的构造和显示顺序

Qml-Item的构造和显示顺序 qml文件中组件构造顺序 在同一个qml文件中&#xff0c;同层级的Item, 文件尾的Item优先构造&#xff0c;文件首的Item后构造。这就能解释默认情况下同一个qml文件中&#xff0c;几个同层级的item都设置了focus:true&#xff0c;为啥最上面item最终有…

毕业设计选题:基于django+vue的个人博客系统设计与开发

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 博主管理 博客文章管理 博文排行管理 博文打赏管理 博文…

设计模式之组合模式(Composite)

一、组合模式介绍 组合模式(Composite Pattern) 的定义是&#xff1a;将对象组合成树形结构以表示整个部分的层 次结构。组合模式可以让用户统一对待单个对象和对象的组合。 如在windows操作系统中的目录结构&#xff0c;其实就是树形目录结构&#xff0c;可以通过 tree /f 命令…

加速“人工智能+”落地,青云如何打磨智算基石

智算中心建设不断加速&#xff0c;正成为推动数字经济发展的新引擎。 根据天风证券的研究&#xff0c;2024年1-6月&#xff0c;全国智算中心招投标相关事件791起&#xff0c;较上年同期增加407.1%。 围绕AI算力&#xff0c;云计算厂商也在积极探索第二增长曲线。根据2024年半年…

2024.10月17日- Vue.js(2)

2.4 计算属性 从字符串反转中&#xff0c;我们发现 插值语法的初衷是用于简单运算。明显练习题中的写法&#xff0c;违背了插值语法的初衷。 methods方法可以。但是方法中如果封装的是性能开销比较大的逻辑代码&#xff0c;需要进行大量的运算&#xff0c;并且别的属性还依赖…

【数据结构与算法】栈和队列

文章目录 一.栈1.1定义 顺序栈和链式栈1.2基本操作1.2.1表示1.2.2初始化1.2.3清空1.2.4销毁1.2.5入栈1.2.6出栈1.2.7取栈顶 1.3共享栈1.3.1定义1.3.2进栈出栈 二.队列2.1定义 顺序队列和链式队列循环队列2.2基本操作2.2.1初始化2.2.2判空2.2.3求队列长度2.2.4取队头元素2.2.5销…

Python123练习题

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;题目解析 目录 &#x1f449;&#x1f3fb;百钱买百鸡&#x1f449;&#x1f3fb;鸡兔同笼&#x1f449;&#x1f3fb;最大公约数和最小公倍数&#x1f449;…

redux与react18setState触发render问题

最近在做一个需求&#xff0c;需要用im做那个协同。 刚好遇到一个比较有意思的问题。 具体问题就不赘述了。 根本原因就是在修改state的时候&#xff0c;触发了两次重渲染。 后面也是做了一些验证 demo function App() {const [state, setState] useState("");con…

JDK、JRE、JVM相关知识点

1、JDK、JRE、JVM三者的关系 JDK‌&#xff1a;Java开发工具包&#xff0c;包括编译工具&#xff08;javac.exe&#xff09;、打包工具&#xff08;jar.exe&#xff09;等&#xff0c;也包含JRE。JDK是开发Java程序的主要工具包&#xff0c;包括了Java运行环境、Java工具和Jav…

C++之设计原则

在C中&#xff0c;设计原则是一套指导软件开发过程中决策和设计模式的准则&#xff0c;旨在提高软件的可维护性、可扩展性、灵活性和可靠性。 以下是几种核心设计原则&#xff1a; 1.单一职责 功能单一&#xff0c;方便组合和复用。 图示&#xff1a; 应用场景&#xff1a;…