投资项目网站建设方案双色调网站

当前位置: 首页 > news >正文

投资项目网站建设方案,双色调网站,外汇网站怎么做优化,网站建设销售渠道序言#xff1a; 在上期#xff0c;我们对多态进行了详细的讲解。本期#xff0c;我给大家带来的是关于有关多态常见的笔试和面试问题#xff0c;帮助大家理解记忆相关知识点。 目录 #xff08;一#xff09;概念查考 #xff08;二#xff09;问答题 1、简述一下…序言 在上期我们对多态进行了详细的讲解。本期我给大家带来的是关于有关多态常见的笔试和面试问题帮助大家理解记忆相关知识点。 目录 一概念查考 二问答题 1、简述一下 C 中的多态 2、什么是重载、重写(覆盖)、重定义(隐藏) 3、C 的重载和重写是如何实现的 4、inline函数可以是虚函数吗 5、静态成员可以是虚函数吗 6、为什么要虚析构而不能虚构造 7、对象访问普通函数快还是虚函数更快 8、虚函数表里存放的内容是什么时候写进去的 9、C 中哪些函数不能被声明为虚函数 一概念查考 1. ( ) 是面向对象程序设计语言中的一种机制。这种机制实现了方法的定义与具体的对象无关 而对方法的调用则可以关联于具体的对象 A: 继承 B: 模板 C: 对象的自身引用 D: 动态绑定 【答案】D 2. 面向对象设计中的继承和组合下面说法错误的是 A继承允许我们覆盖重写父类的实现细节父类的实现对于子类是可见的是一种静态复 用也称为白盒复用 B组合的对象不需要关心各自的实现细节之间的关系是在运行时候才确定的是一种动 态复用也称为黑盒复用 C优先使用继承而不是组合是面向对象设计的第二原则 D继承可以使子类能自动继承父类的接口但在设计模式中认为这是一种破坏了父类的封 装性的表现 【答案】C 在面向对象设计中并没有规定继承应该优先于组合。事实上面向对象设计原则中的一个重要原则是「优先使用组合而不是继承」也被称为组合优于继承原则 这个原则建议使用组合或聚合关系来构建对象之间的关联而不是过度使用继承。通过组合可以更灵活地构建对象之间的关系避免了过度依赖继承关系导致的紧耦合和复杂性增加。 因此选项C中的说法是错误的。 3. 以下关于纯虚函数的说法 , 正确的是 ( ) A声明纯虚函数的类不能实例化对象 B声明纯虚函数的类是虚基类 C子类必须实现基类的纯虚函数D纯虚函数必须是空函数 【答案】A 纯虚函数是在基类中声明的虚函数它没有提供默认的实现而是由派生类来实现。这个函数在基类中被声明为纯虚函数可以通过在函数声明的末尾使用 0 来表示。 根据C语言规范含有纯虚函数的类被称为抽象类不能被直接实例化对象。只能通过派生类来创建对象派生类需要实现基类中的纯虚函数才能够被实例化。 选项B、C、D的说法是不正确的 B声明纯虚函数的类并不一定是虚基类虚基类是指在多重继承中被标记为虚继承的类。C子类可以选择是否实现基类的纯虚函数如果子类不实现基类中的纯虚函数则它也会成为一个抽象类不能直接实例化对象。D纯虚函数可以有自己的实现代码不一定是空函数。只要在基类中将该函数声明为纯虚函数即可。 4.关于虚函数的描述正确的是( )
A派生类的虚函数与基类的虚函数具有不同的参数个数和类型 B内联函数不能是虚函数 C派生类必须重新定义基类的虚函数 D虚函数可以是一个static型的函数 【答案】B 选项A是错误的描述。派生类的虚函数与基类的虚函数具有相同的参数个数和类型。在C中虚函数实现了动态绑定允许基类指针或引用在运行时动态地调用派生类的虚函数。 选项C也是错误的描述。派生类不一定必须重新定义基类的虚函数。如果派生类没有重新定义基类的虚函数那么它将继承基类的虚函数实现。 选项D也是错误的描述。虚函数不能是一个static型的函数。虚函数通过对象的动态类型来确定调用的函数而static函数是与类本身相关联的不涉及对象的特定实例。 因此正确的描述是 B内联函数不能是虚函数。 5.关于虚表说法正确的是
A一个类只能有一张虚表 B基类中有虚函数如果子类中没有重写基类的虚函数此时子类与基类共用同一张虚表 C虚表是在运行期间动态生成的 D一个类的不同对象共享该类的虚表 【答案】D A一个类的每个实例都有自己的虚函数表指针vptr即每个对象都有一个指向虚函数表的指针。每个类只有一个虚函数表但每个对象都有自己的vptr可以指向该类的虚函数表。 B当派生类继承基类的虚函数并在派生类中没有重写时子类将共享基类的虚函数表并且派生类对象的vptr指针将指向基类的虚函数表。 C虚表是在编译阶段由编译器生成的静态数据结构。编译器根据类的层次结构和虚函数的声明创建虚函数表并在每个对象的vptr中存储对应的虚函数表。 因此选项 B、C 和 A 都是错误的。只有选项 D 是正确的即一个类的不同对象共享该类的虚表。 6.假设A类中有虚函数B继承自AB重写A中的虚函数也没有定义任何虚函数则
AA类对象的前4个字节存储虚表地址B类对象前4个字节不是虚表地址 BA类对象和B类对象前4个字节存储的都是虚基表的地址 CA类对象和B类对象前4个字节存储的虚表地址相同 DA类和B类虚表中虚函数个数相同但A类和B类使用的不是同一张虚表 【答案】D 在这种情况下类A和类B各自会有自己的虚函数表虽然它们的虚函数个数相同但它们使用的是不同的虚表。类B继承自类A当类B重写了类A中的虚函数时会在类B的虚函数表中将该虚函数替换为类B的实现。因此类A和类B的虚表中存储的是不同的虚函数的地址。 选项A、B和C均与这种情况不符因此是错误的。正确选项是D。 7.下面程序输出结果是什么? class A{ public:A(char *s) { coutsendl; }~A(){} }; class B:virtual public A { public:B(char *s1,char*s2):A(s1) { couts2endl; } }; class C:virtual public A { public:C(char *s1,char*s2):A(s1) { couts2endl; } }; class D:public B,public C { public:D(char *s1,char *s2,char *s3,char *s4):B(s1,s2),C(s1,s3),A(s1){ couts4endl;} }; int main() {D pnew D(class A,class B,class C,class D);delete p;return 0; } Aclass A class B class C class D Bclass D class B class C class A Cclass D class C class B class ADclass A class C class B class D 【答案】A 首先通过创建D类的对象p并传递相应的字符串参数会触发类的构造函数的调用。因为D类是继承自B和C类并且B和C类都是虚继承自A类的所以在创建D对象时会按照虚继承的顺序先调用A类的构造函数。因此首先输出 class A。 接下来B类和C类的构造函数会被调用分别输出 class B 和 class C。 最后D类自身的构造函数被调用输出 class D。 在程序结束时会执行delete操作释放对象p所占用的内存同时会按照与构造函数相反的顺序调用析构函数所以会先输出 class D、class C、class B、class A。 二问答题 1、简述一下 C 中的多态 由于 派生类重写基类方法然后用基类引用指向派生类对象调用方法时候会进行动态绑定这就是多态 。 多态分为 静态多态和动态多态 1. 静态多态编译器在编译期间完成的编译器会根据实参类型来推断该调用哪个函数如果有对应 的函数就调用没有则在编译时报错。 比如一个简单的加法函数 includeiostream using namespace std;int Add(int a,int b)//1 {return ab; } char Add(char a,char b)//2 {return ab; } int main() {coutAdd(666,888)endl;//1coutAdd(1,2);//2return 0; } 【说明】 显然第一条语句会调用函数1而第二条语句会调用函数2这绝不是因为函数的声明顺序不信你可以将顺序调过来试试。 2.动态多态其实要实现动态多态需要几个条件——即动态绑定条件 1. 虚函数。基类中必须有虚函数在派生类中必须重写虚函数。 2. 通过基类类型的指针或引用来调用虚函数。 2、什么是重载、重写(覆盖)、重定义(隐藏) 具体见我上一篇博客C 的重载和重写以及它们的区别 3、C 的重载和重写是如何实现的 1. 重载 C 利用命名倾轧 name mangling 技术来改名函数名区分参数不同的同名函数。命名倾轧是在编译阶段完成的。 编译时将参数类型加入以区分不同。 2. 重写 在 基类的函数前加上 virtual 关键字在派生类中重写该函数运行时将会根据对象的实际类 型来调用相应的函数 。如果 对象类型是派生类就调用派生类的函数如果对象类型是基类就调用基类的函数。 虚函数需要注意的地方 用virtual关键字申明的函数叫做虚函数虚函数肯定是类的成员函数。 存在虚函数的类都有一个一维的虚函数表叫做虚表类的对象有一个指向虚表开始的虚指针。 虚表是和类对应的虚表指针是和对象对应的。 多态性是一个接口多种实现是面向对象的核心分为类的多态性和函数的多态性。 重写用虚函数来实现结合动态绑定。 纯虚函数是虚函数再加上 0。 抽象类是指包括至少一个纯虚函数的类。 纯虚函数 virtual void fun()0 。即抽象类 必须在子类实现这个函数即先有名称没有内容 在 派生类实现内容。 4、inline函数可以是虚函数吗 可以不过编译器就忽略 inline 属性这个函数就不再是 inline因为虚函数要放到虚表中去 在C中虚函数是用于实现运行时多态性的概念而虚函数的调用是通过指针或引用来完成的。然而内联函数inline function在编译时被展开将函数的代码插入到调用处以减少函数调用的开销。 由于内联函数的展开是在编译时完成的而虚函数的调用是在运行时动态确定的因此二者的机制存在冲突。虚函数的调用需要根据对象的实际类型确定调用的函数而内联函数的展开则需要在编译时就确定函数体的具体代码。 当我们将虚函数声明为内联函数时编译器会忽略内联关键字而将其作为普通的成员函数对待。下面是一个示例代码展示了虚函数声明为内联函数的情况
class Base { public:virtual inline void foo() {cout Base::foo() endl;} };class Derived : public Base { public:inline void foo() override {cout Derived::foo() endl;} };int main() {Base
ptr new Derived();ptr-foo();delete ptr;return 0; }【说明】 在这个示例中我们将基类 Base 中的虚函数 foo 声明为内联函数并在派生类 Derived 中进行重写。然后我们通过基类的指针调用虚函数 foo。尽管我们在虚函数声明中加上了 inline 关键字但编译器会忽略它将其视为普通的成员函数。因此在运行时仍然会根据实际类型进行动态绑定。 输出结果将是  这表明虚函数 foo 被正确地重写并且运行时根据实际对象类型进行了动态绑定。 5、静态成员可以是虚函数吗 静态成员函数不能被声明为虚函数。虚函数的多态性是通过在运行时动态绑定来实现的而静态成员函数是与类本身相关联的不依赖于特定的对象因此无法进行动态绑定。 虚函数依赖于对象的虚表通过对象的指针或引用来调用虚函数时会根据对象的实际类型确定要调用的具体函数。而静态成员函数是与类关联而不是与对象关联的它可以通过类名直接调用不依赖于特定的对象。 另外静态成员函数没有 this 指针使用类型::成员函数的调用方式无法访问虚函数表所以静态成员函数无法放进虚函数表。 综上所述静态成员函数不能被声明为虚函数。 以下是一个示例代码演示了将静态成员函数声明为虚函数将导致编译错误 class Base { public:virtual void foo() {std::cout Base::foo() std::endl;}static virtual void bar() // 尝试将静态成员函数声明为虚函数{std::cout Base::bar() std::endl;} };int main() {Base::bar(); // 直接通过类名调用静态成员函数return 0; } 上述代码中我们尝试将静态成员函数bar()声明为虚函数。如果我们尝试编译这段代码编译器将给出一个错误。 这个错误告诉我们无法将静态成员函数声明为虚函数。  6、为什么要虚析构而不能虚构造 1. 虚析构 将可能会被继承的父类的析构函数设置为虚函数可以保证当我们 new 一个子类然后使 用基类指针指向该子类对象释放基类指针时可以释放掉子类的空间防止内存泄漏 。如果基类的 析构函数不是虚函数 在特定情况下会 导致派生类无法被析构 。 1. 用派生类类型指针绑定派生类实例析构的时候不管基类析构函数是不是虚函数都会正常析构 2. 用基类类型指针绑定派生类实例析构的时候如果基类析构函数不是虚函数则只会析构基 类不会析构派生类对象从而造成内存泄漏。为什么会出现这种现象呢个人认为析构的时 候如果没有虚函数的动态绑定功能就只根据指针的类型来进行的而不是根据指针绑定的对 象来进行所以只是调用了基类的析构函数如果基类的析构函数是虚函数则析构的时候就要根据指针绑定的对象来调用对应的析构函数了。 C 默认的析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚表指针占用额外的内存。 代码说明 我们创建一个 TimeKeeper 基类和一些及其它的派生类作为不同的计时方法 class TimeKeeper { public:TimeKeeper() {}~TimeKeeper() {} //非virtual的 }; //都继承与TimeKeeper class AtomicClock :public TimeKeeper{}; class WaterClock :public TimeKeeper {}; class WristWatch :public TimeKeeper {}; 如果客户想要在程序中使用时间不想操作时间如何计算等细节这时候我们可以设计 factory 工 厂函数让函数返回指针指向一个计时对象。该函数返回一个基类指针这个基类指针是指向于 派生类对象的 TimeKeeper* getTimeKeeper() {//返回一个指针指向一个TimeKeeper派生类的动态分配对象 } 因为函数返回的对象存在于堆中因此为了 在不使用时我们需要使用释放该对象 delete TimeKeeper* ptk getTimeKeeper();delete ptk; 此处基类的析构函数是非 virtual 的因此 通过一个基类指针删除派生类对象是错误的 解决办法 将基类的析构函数改为 virtual 就正确了 class TimeKeeper { public:TimeKeeper() {}virtual ~TimeKeeper() {} }; 声明为 virtual 之后通过 基类指针删除派生类对象就会释放整个对象基类 派生类 2. 不能虚构造 1. 从存储空间角度虚函数对应一个vtale,这个表的地址是存储在对象的内存空间的。如果将构造函数设置为虚函数就需要到vtable 中调用可是对象还没有实例化没有内存空间分 配如何调用。悖论 2. 从实现上看vbtl 在构造函数调用后才建立因而构造函数不可能成为虚函数。 7、对象访问普通函数快还是虚函数更快 首先如果是普通对象是一样快的如果是指针对象或者是引用对象则调用的普通函数快因为构成多态运行时调用虚函数需要到虚函数表中去查找。 8、虚函数表里存放的内容是什么时候写进去的 1. 虚函数表是一个存储虚函数地址的数组,以NULL结尾。虚表vftable在编译阶段生成对象内存空间开辟以后写入对象中的 vfptr然后调用构造函数。即虚表在构造函数之前写入 2. 除了在构造函数之前写入之外我们还需要考虑到虚表的二次写入机制通过此机制让每个对象的虚表指针都能准确的指向到自己类的虚表为实现动多态提供支持。 9、C 中哪些函数不能被声明为虚函数 常见的不不能声明为虚函数的有 普通函数非成员函数静态成员函数内联成员函数构造函数友元函数。 1. 为什么C不支持普通函数为虚函数 普通函数非成员函数只能被overload不能被override声明为虚函数无意义因此编译器会在编译时绑定函数。 2. 为什么C不支持友元函数为虚函数 因为C不支持友元函数的继承对于没有继承特性的函数没有虚函数的说法。 其余的上述我都有讲解。 总结 多态作为笔试重点考察对象希望大家能够勤加练习掌握相关的知识。