《Effective C++》《继承与面向对象设计——33、避免遮掩继承而来的名称》

本文主要是介绍《Effective C++》《继承与面向对象设计——33、避免遮掩继承而来的名称》,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 1、term53:Avoid hiding inherited names
    • 前言:
    • 1.1、同名全局变量在局部作用域中被隐藏
    • 1.2、继承中的隐藏
    • 1.3、进一步论证——继承中的函数的隐藏
    • 1.4、如何将隐藏的行为进行覆盖
    • 1.4.1 通过using声明增加对基类成员函数的使用
    • 1.4.2 使用forwarding函数
  • 2、面试相关
    • 2.1什么是名称遮掩(Name Hiding)?
    • 2.2 在派生类中定义一个与基类同名的成员函数会发生什么?
    • 2.3 如何避免名称遮掩?
    • 2.4 如果派生类中的函数遮掩了基类中的函数,但你又想在派生类中调用基类版本的函数,你应该怎么做?
    • 2.5 什么是虚函数和重写(Override)?
    • 2.6 如何在派生类中扩展而不是替换基类的功能?
    • 2.7 为什么使用虚函数通常比非虚函数更安全?
    • 2.8你如何在代码审查中识别名称遮掩的问题?
    • 2.9 如果基类有一个保护成员,派生类不小心定义了一个同名的公有成员,会发生什么?
    • 2.10在继承中,你更倾向于使用公有继承、保护继承还是私有继承?为什么?
  • 3、总结
  • 4、参考

1、term53:Avoid hiding inherited names

前言:

在C++中,继承是一个强大的特性,它允许一个类(派生类或子类)继承另一个类(基类或父类)的成员变量和成员函数。然而,在继承的过程中,有时会出现基类成员被派生类成员 “隐藏” 的情况。这主要发生在派生类定义了与基类同名的成员时。

隐藏的概念
当派生类定义了一个与基类同名的成员(无论是数据成员还是成员函数),基类的该成员在派生类中就会被隐藏。这意味着,当通过派生类的对象访问这个同名成员时,只能访问到派生类自己的成员,而无法直接访问到基类的同名成员。
隐藏的例子
下面是一个简单的例子来说明继承中的隐藏:

class Base {  
public:  void show() {  std::cout << "Base::show()" << std::endl;  }  
};  class Derived : public Base {  
public:  void show() { // 与基类中的show函数同名  std::cout << "Derived::show()" << std::endl;  }  int value; // 与基类可能存在的同名数据成员  
};  int main() {  Derived d;  d.show(); // 输出:Derived::show()  // d.Base::show(); // 如果需要访问基类的show函数,需要这样显式指定  return 0;  
}

在上面的例子中,Derived 类继承了 Base 类,并定义了一个与 Base 类中同名的 show 函数。当通过 Derived 类的对象 d 调用 show 函数时,调用的是 Derived 类中定义的 show 函数,而不是 Base 类中的。如果想要访问基类的 show 函数,需要显式地使用作用域解析运算符 :: 来指定。

注意点

  • (1)隐藏不是重写(Override):重写是发生在派生类中的函数与基类中的虚函数具有相同的签名,并且派生类意图替换基类的虚函数实现。隐藏不涉及虚函数,只是简单的同名成员覆盖。
  • (2)数据成员的隐藏:如果派生类定义了一个与基类同名的数据成员,那么基类的数据成员在派生类中也会被隐藏。通过派生类的对象,只能访问到派生类的该数据成员。
  • (3)通过基类指针或引用访问:如果有一个指向派生类对象的基类指针或引用,那么通过这个指针或引用访问的将是基类的成员,而不是派生类中被隐藏的成员。

如何避免隐藏
为了避免意外的隐藏,应该仔细考虑在派生类中定义的成员是否与基类中的成员同名。如果确实需要覆盖基类的成员函数,应该确保基类中的函数是虚函数,并使用相同的签名在派生类中定义。对于数据成员,如果可能的话,最好避免使用与基类同名的数据成员,或者使用不同的名称来避免混淆。

1.1、同名全局变量在局部作用域中被隐藏

名称其实和继承无关,而是和作用域(scopes)有关。如下面这段代码:

#include <iostream>
using namespace std;int x = 10; //全局变量void someFunc()
{double x = 5.0;      //局部变量cout << x;           //输出局部变量
}int main(){someFunc();return 0;
}

当全局和局部存在相同的变量时,在局部作用域中,全局作用域的变量名会被隐藏,优先使用局部的变量。
在这里插入图片描述

图1. 局部和全局作用域

C++的名称遮掩规则所做的唯一事情是:遮掩名称。至于名称是否是同一类型,并不重要。

1.2、继承中的隐藏

现在进入继承。我们知道当我们处在一个派生类成员函数内部时,并且指向了一些基类的东西(例如,一个成员函数,一个typedef或者一个数据成员),编译器能够找到我们所指向的东西,因为派生类继承了声明在基类中的这些东西。实际的工作方式是派生类的作用域被嵌套在基类作用域内部。举个例子:

class Base
{
private:int x;
public:virtual void mf1() = 0;virtual void mf2();void mf3();...
};class Derived :public Base
{
public:virtual void mf1(); //重写(覆盖)void mf4();...
};

在这里插入图片描述

图2.基类和派生类的作用域
这个类混合了public和private名称,以及一组成员变量和成员函数的名称。成员函数包括纯虚函数,普通虚函数(非纯虚函数)以及非虚函数。在这次的讨论中我们唯一关心的是他们是名称。至于他们是什么样的类型无关紧要。这个例子使用的是单继承,但是一旦你明白了在单继承下会发生什么,多继承下的C++行为很容易就能够预推测出来。 假设派生类中的mf4定义如下:
void Derived::mf4()
{...mf2();...
}

当编译器看到这里使用了名字mf2,它们必须理解mf2指向的是什么。它们会在作用域中寻找名字为mf2的一个声明。

在Derived的fm4()函数中调用了fm2()函数,对于fm2()函数的查找顺序如下:

  • ① 先在fm4()函数中查找,如果没有进行②;
  • ② 然后在Derived类中查找,如果没有进行③;
  • ③ 然后在基类Base中查找(查找到了就调用基类中的Base);
  • ④ 假设在Base中还没有查找到,那么就在Base所在的namespace中查找;如果还有没继续在全局作用域查找;

1.3、进一步论证——继承中的函数的隐藏

考虑前面的例子,这次我们除了要重载mf1和mf3之外,还在Derived中添加一个mf3版本。(正如条款36中解释的,Derived中mf3的声明——一个继承而来的非virtual函数——会让这个设计看起来很可疑,但是为了理解继承下的名字可见性,我们忽略这个问题。)

class Base
{
private:int x;
public:virtual void mf1() = 0;virtual void mf1(int);virtual void mf2();void mf3();void mf3(double);...
};class Derived :public Base
{
public:virtual void mf1(); //基类中的所有mf1()都被隐藏void mf3();         //基类中的所有fm3()都被隐藏void mf4();...
};

现在使用下面代码进行调用:

Derived d;
int x;
... 
d.mf1();  //正确
d.mf1(x); //错误,被隐藏了
d.mf2();  //正确
d.mf3();  //正确
d.mf3(x); //错误,被隐藏了

可以看到,对于有相同名字的基类和派生类中的函数,即使参数类型不同,上面的隐藏规则也同样适用,并且它和函数的虚与非虚没有关系。在这个条款开始也是同样的方式,函数someFunc中的double x隐藏了全局作用域的int x,在这里Derived中的函数mf3隐藏了基类中名字为mf3的函数,即使参数类型不一样。

1.4、如何将隐藏的行为进行覆盖

1.4.1 通过using声明增加对基类成员函数的使用

有时隐藏可能会违反基类与派生类之间的is-a关系。因此我们可以使用using声明表达式取消这种隐藏,在派生类中导入基类的函数行为。如下面例子:

#include <iostream>  
using namespace std;  
class Base {  
private:  int x;  
public:  // 纯虚函数,需要在派生类中提供实现  virtual void mf1() = 0;  // 重载版本的mf1函数  virtual void mf1(int val) {  std::cout << "   Base::mf1(int): " << val << std::endl;  }  // 另一个虚函数  virtual void mf2() {  std::cout << "   Base::mf2()" << std::endl;  }  // 非虚函数  void mf3() {  std::cout << "   Base::mf3()" << std::endl;  }  // 重载版本的mf3函数  void mf3(double val) {  std::cout << "   Base::mf3(double): " << val << std::endl;  }  // ... 其他成员函数和数据 ...  
};  class Derived : public Base {  
public:  using Base::mf1; // 使得Base的所有mf1版本在Derived中可见  using Base::mf3; // 使得Base的所有mf3版本在Derived中可见  // 重写纯虚函数mf1()  virtual void mf1() override {  std::cout << "Derived::mf1()" << std::endl;  }  // 隐藏了Base中的非虚函数mf3(),但是mf3(double)没有隐藏,因为使用了using声明  void mf3()  { // 注意:这里使用override是因为mf3在Base中是虚函数  std::cout << "Derived::mf3()" << std::endl;  }  // 新增的成员函数  void mf4() {  std::cout << "Derived::mf4()" << std::endl;  }  // ... 其他成员函数和数据 ...  
};  int main() {  Derived d;  d.mf1();        // 调用Derived中重写的mf1  d.mf1(10);      // 调用Base中重载的mf1(int)  d.mf3();        // 调用Derived中重写的mf3  d.mf3(3.14);    // 调用Base中重载的mf3(double)  d.mf2();        // 调用Base中的mf2  d.mf4();        // 调用Derived中新增的mf4  return 0;  
}

输出内容如下:

Derived::mf1()Base::mf1(int): 10
Derived::mf3()Base::mf3(double): 3.14Base::mf2()
Derived::mf4()

如果你的继承基类并加上重载函数,你想对其中的一些函数进行重新定义或者覆盖,你需要为每个即将被隐藏掉的名字包含一个using声明,如果你不这样做,你想继承的一些名字就会被隐藏。

1.4.2 使用forwarding函数

有时候你并不想继承基类的所有函数。在public继承下,这绝对不可能发生,因为它违反了基类和派生类之间public继承的”is-a”关系。(这也是为什么上面的using声明放在派生类的public部分:基类中的public名字在public继承的派生类中应该也是public的)。然而在private继承中(见条款39),它也是有意义的。举个例子,假设Derived私有继承自基类Base,Derived类想继承基类函数mf1的唯一版本是不带参数的版本。Using声明在这里就不工作了,因为一个using声明会使得所有继承而来的函数的名字在派生类中是可见的。这里可以使用不同的技术,也就是简单的forwarding函数:

#include <iostream>  
using namespace std;  
class Base {  
private:  int x;  
public:  // 纯虚函数,需要在派生类中提供实现  //virtual void mf1() = 0;  virtual void mf1() {  std::cout << "   Base::mf1() "  << std::endl;  }  // 重载版本的mf1函数  virtual void mf1(int val) {  std::cout << "   Base::mf1(int): " << val << std::endl;  }  // ... 其他成员函数和数据 ...  
};  class Derived : private Base {  
public:  virtual void mf1()  {  Base::mf1();}  // ... 其他成员函数和数据 ...  
};  int main() {  Derived d;  d.mf1();        // true 调用Derived中重写的mf1  //d.mf1(10);    // false Base::mf1(int) 被遮掩住了return 0;  
}

输出内容:

Base::mf1() 

inline转交函数的另一个用途是为那些不支持using声明式(注:这并非正确行为)的老旧编译器另开了一条新路,将继承而得的名称汇入派生类作用域内。当继承同模板结合起来的时候,一个完全不同的“继承而来的名字被隐藏”问题就会出现,详情见 条款43。

2、面试相关

以下是一些与避免遮掩继承而来的名称相关的C++面试问题:

2.1什么是名称遮掩(Name Hiding)?

解释名称遮掩的概念,以及它如何在继承中发生。
(1)名称遮掩(Name Hiding)在C++中是一个重要的概念,它涉及到在派生类中定义的成员与基类中的同名成员之间的交互。当一个派生类引入了一个新的成员(无论是变量、函数还是类型别名),而这个成员的名字与基类中的某个成员名字相同时,就会发生名称遮掩。
具体来说,当派生类中的成员与基类中的成员具有相同的名称时,派生类中的成员会“遮掩”或“隐藏”基类中的同名成员。这意味着,在派生类的上下文中,如果试图访问这个被遮掩的名称,将默认解析为派生类中的成员,而不是基类中的成员。
这种遮掩行为可能会导致一些非直观或意外的结果,特别是当程序员意图调用基类版本的成员时。由于派生类的成员遮掩了基类的同名成员,如果没有明确的指定,编译器将不会考虑基类的成员。
名称遮掩通常发生在以下情况:
(1)成员函数遮掩:当派生类定义了一个与基类成员函数同名的新函数时,基类中的该函数在派生类的作用域内将被遮掩。

class Base {  
public:  void func() { /* Base implementation */ }  
};  class Derived : public Base {  
public:  void func() { /* Derived implementation */ } // 遮掩了Base::func()  
};

在上面的例子中,Derived::func()遮掩了Base::func()。在Derived类的上下文中调用func()将执行Derived版本的函数。
(2)数据成员遮掩:当派生类定义了一个与基类数据成员同名的数据成员时,也会发生名称遮掩。

class Base {  
public:  int value;  
};  class Derived : public Base {  
public:  double value; // 遮掩了Base::value  
};

在Derived类中,value指的是double类型的成员,而不是基类中的int类型成员。
为了避免名称遮掩带来的问题,程序员应该谨慎选择成员名称,并尽量避免在派生类和基类中使用相同的名称。如果确实需要访问被遮掩的基类成员,可以使用作用域解析运算符(::)来明确指定要访问的是基类中的成员。
例如:

class Derived : public Base {  
public:  void useBaseFunc() {  Base::func(); // 显式调用基类中的func()  }  
};

在useBaseFunc()函数中,通过Base::func()可以明确地调用基类中的func()函数,即使它在派生类中被遮掩了。

2.2 在派生类中定义一个与基类同名的成员函数会发生什么?

讨论这种情况下会发生什么,以及它如何影响程序的行为。

2.3 如何避免名称遮掩?

讨论避免名称遮掩的最佳实践,如使用作用域解析运算符(::)来明确指定要调用的基类函数。

2.4 如果派生类中的函数遮掩了基类中的函数,但你又想在派生类中调用基类版本的函数,你应该怎么做?

解释如何使用作用域解析运算符来调用基类中被遮掩的函数。

2.5 什么是虚函数和重写(Override)?

讨论虚函数的概念,以及如何在派生类中重写基类的虚函数。这有助于理解如何在不遮掩基类函数的情况下扩展类的功能。

2.6 如何在派生类中扩展而不是替换基类的功能?

讨论如何在派生类中调用基类的函数,并在其基础上添加新的功能,而不是完全替换它。

2.7 为什么使用虚函数通常比非虚函数更安全?

解释虚函数如何在运行时确定要调用的函数版本,从而避免名称遮掩可能导致的问题。

2.8你如何在代码审查中识别名称遮掩的问题?

讨论代码审查过程中如何识别和解决名称遮掩问题的方法。
在代码审查过程中,识别和解决名称遮掩问题是非常重要的,因为这有助于维护代码的可读性、可维护性和正确性。以下是一些建议的方法,用于在代码审查中识别和解决名称遮掩问题:

  1. 仔细阅读代码和文档
  • 理解继承关系:首先,审查者需要理解代码中的继承关系,包括哪些类是从其他类继承而来的,以及它们是如何相互关联的。
  • 查看类定义:审查基类和派生类的成员列表,特别是那些具有相同名称的成员。
  • 阅读注释和文档:如果有相关的注释和文档,它们可能会提供关于成员名称选择的理由和意图,从而帮助识别可能的名称遮掩问题。
  1. 使用静态分析工具
  • 集成开发环境(IDE):许多IDE都提供了静态分析工具,可以自动检测名称遮掩等潜在问题。
  • 静态代码分析工具:使用专门的静态代码分析工具来扫描代码库,这些工具可以报告名称遮掩等常见的编程错误。
  1. 检查函数调用和成员访问
  • 检查派生类成员:在派生类中查找对基类成员的引用。如果使用了与基类成员相同的名称,但没有使用作用域解析运算符(::),则可能发生名称遮掩。
  • 检查作用域:注意代码中的作用域范围,特别是在嵌套类或函数内部。名称遮掩可能在这些情况下更加隐蔽。
  1. 讨论和沟通
  • 与团队成员交流:与代码的作者或其他团队成员讨论名称选择的原因,了解他们是否意识到名称遮掩的问题。
  • 提出改进建议:如果发现了名称遮掩问题,提出明确的改进建议,例如重命名派生类成员、使用作用域解析运算符或考虑其他设计选择。
  1. 制定编码规范
  • 避免使用相同名称:在编码规范中明确规定,尽量避免在基类和派生类中使用相同的成员名称。
  • 使用描述性名称:鼓励使用描述性强的成员名称,以减少名称冲突和遮掩的可能性。
  1. 审查重构代码
  • 重构机会:如果在代码审查过程中发现大量的名称遮掩问题,这可能是一个重构代码的好机会。通过重构,可以消除名称遮掩问题,并改进代码的整体结构和可读性。

通过结合这些方法,代码审查者可以更有效地识别和解决名称遮掩问题,从而提高代码的质量和可维护性。

2.9 如果基类有一个保护成员,派生类不小心定义了一个同名的公有成员,会发生什么?

讨论这种情况下的行为,以及它如何影响类的封装性和访问控制。

2.10在继承中,你更倾向于使用公有继承、保护继承还是私有继承?为什么?

讨论不同继承类型的优缺点,以及它们如何影响名称遮掩和类的设计。

在继承中,我倾向于使用公有继承(public inheritance)、保护继承(protected inheritance)还是私有继承(private inheritance)取决于具体的设计需求和目的。每种继承方式都有其特定的用途和优缺点,下面我会逐一解释并说明我的倾向。

  1. 公有继承(Public Inheritance)

公有继承是最常用的一种继承方式。当派生类公有继承基类时,基类的公有成员在派生类中仍然是公有的,基类的保护成员在派生类中变为保护成员,而基类的私有成员在派生类中是不可访问的。

我倾向于在以下情况下使用公有继承:

  • 当派生类需要作为基类的一个特定类型的对象使用时。
  • 当派生类需要扩展基类的接口,并允许客户端代码通过派生类访问基类的公有成员时。

公有继承保持了基类接口的完整性,并允许派生类添加新的功能或特性。它适用于“is-a”关系,即派生类确实是基类的一种特殊类型。

  1. 保护继承(Protected Inheritance)

保护继承相对较少使用。当派生类保护继承基类时,基类的公有和保护成员在派生类中都变成保护的,而基类的私有成员仍然是不可访问的。

我倾向于在以下情况下使用保护继承:

  • 当派生类需要访问基类的受保护成员和私有成员,但又不希望这些成员在派生类的外部可见时。
  • 当实现一种更复杂的继承层次结构,并且需要在多个层次之间共享某些数据或功能时。

保护继承提供了一种在派生类中封装基类实现细节的方式,同时允许派生类扩展和定制这些实现。然而,它通常比公有继承更加复杂,并且需要谨慎使用以避免引入不必要的复杂性。

  1. 私有继承(Private Inheritance)

私有继承是最不常用的一种继承方式。当派生类私有继承基类时,基类的所有成员(包括公有、保护和私有成员)在派生类中都变成私有的。

我倾向于在以下情况下使用私有继承:

  • 当派生类需要利用基类的实现细节,但这些细节不应该暴露给派生类的外部用户时。
  • 当实现一种“has-a”关系,即派生类包含一个基类的对象作为其实现的一部分,但不需要继承基类的接口时。

私有继承允许派生类访问和利用基类的内部实现,同时隐藏了这些实现细节。它适用于那些更关注实现复用而不是接口扩展的场景。然而,由于它隐藏了基类的接口,因此在使用时需要格外小心,以避免违反封装原则。

综上所述,我的倾向取决于具体的设计需求。在大多数情况下,我会选择公有继承,因为它保持了基类接口的完整性,并允许派生类扩展和定制这些接口。然而,在某些特殊情况下,我也可能会考虑使用保护继承或私有继承来满足特定的设计需求。

3、总结

天堂有路你不走,地狱无门你自来。

4、参考

4.1 《Effective C++》
4.2 Effective C++条款33:避免遮掩继承而来的名称(Avoid hiding inherited names)

这篇关于《Effective C++》《继承与面向对象设计——33、避免遮掩继承而来的名称》的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/864014

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

怎么让1台电脑共享给7人同时流畅设计

在当今的创意设计与数字内容生产领域,图形工作站以其强大的计算能力、专业的图形处理能力和稳定的系统性能,成为了众多设计师、动画师、视频编辑师等创意工作者的必备工具。 设计团队面临资源有限,比如只有一台高性能电脑时,如何高效地让七人同时流畅地进行设计工作,便成为了一个亟待解决的问题。 一、硬件升级与配置 1.高性能处理器(CPU):选择多核、高线程的处理器,例如Intel的至强系列或AMD的Ry

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现