-
C++ Primer Plus 6 关于虚函数的描述勘误
2017-06-28 11:27:25最近再读一遍C++Primer Plus,在虚函数描述(中文版P501)中有以下描述: 如果数组成员指向的是Brass对象,则调用Brass::ViewAcct();如果指向的的是BrassPlus对象,则调用BrasPlus::ViewAcct()。如果Brass::...最近再读一遍C++Primer Plus,在虚函数描述(中文版P501)中有以下描述:
如果数组成员指向的是Brass对象,则调用Brass::ViewAcct();如果指向的的是BrassPlus对象,则调用BrasPlus::ViewAcct()。如果Brass::ViewAcct()被声明为虚的,则在任何情况下都将调用Brass::ViewAcct()。
个人觉得这句话描述为有应该是:
如果Brass::ViewAcct()未被声明为虚的,则在任何情况下都将调用Brass::ViewAcct()。
特地翻阅了英文原版的描述,发现和译本意思相同,可能是原版就有这个问题,而译者翻译这段时并未仔细考虑,网上搜索了一下,发现也有网友提出此类疑问,特写此帖说明。
英文描述:
If the array member points to a Brassobject,Brass::ViewAcct() is invoked;if the array member points to a BrassPlusobject,BrassPlus::ViewAcct() is invoked. If Brass::ViewAcct() were been declared as virtual, Brass:ViewAcct() would be invoked in all cases.
-
继承中的虚函数与非虚函数
2018-01-09 18:06:20在看公司代码时, 发现了一处关于虚函数的我比较难以理解的地方,大致描述如下: 子类继承父类, 包括继承了虚函数和非虚函数 子类调用父类中的非虚函数 Base::PrintWord(), 在这个非虚函数里它又调用了虚函数...在看公司代码时, 发现了一处关于虚函数的我比较难以理解的地方,大致描述如下:
- 子类继承父类, 包括继承了虚函数和非虚函数
- 子类调用父类中的非虚函数
Base::PrintWord()
, 在这个非虚函数里它又调用了虚函数doPrintWord()
。 - 实验表明调用的虚函数执行的是重写的子类虚函数。
大致的代码可以简写为以下一个例子:
#include <iostream> using namespace std; class Base{ public: void PrintWord(const char* c) { printf("called in Base::PrintWord, and pointer of this is : 0x%x\n",this); doPrintWord(c); } virtual void doPrintWord(const char* c) { printf("%s",c); printf("Base here\n"); printf("Base : 虚函数的this指针地址:0x%x \n\n",this); } }; class Derived: public Base { public: void testprint(const char* c) { Base::PrintWord(c); } private: void doPrintWord(const char* c) { printf("%s",c); printf("Derived here\n"); printf("子类虚函数doPrintWord的this指针地址:0x%x \n\n",this); } }; int main(int argc, char *argv[]) { Derived ss; ss.testprint(""); printf("Derived : 0x%x\n",&ss); return 0; }
为了解决这一疑问,有必要理清虚函数和非虚函数的区别:
虚函数: 虚函数其实是一种动态绑定机制,因为在编译时,编译器是不知道是该调用父类中的虚函数还是子类中的虚函数的,而是在程序执行过程中,动态确定的。虚函数的本质是,C++编译器透过某个表格,在执行时期「间接」调用实际上欲绑定的函数(注意「间接」这个字眼)。这样的表格称为虚拟函数表(常被称为vtable)。每一个「内含虚拟函数的类」,编译器都会为它做出一个虚拟函数表,表中的每一笔元素都指向一个虚拟函数的地址。此外,编译器当然也会为类别加上一项成员变量,是一个指向该虚拟函数表的指针(常被称为vptr)。
每一个由此类衍生出来的对象,都有这么一个vptr。当我们透过这个对象调用虚拟函数,事实上是透过vptr 找到虚拟函数表,再找出虚拟函数的真正地址。如何理解上面的间接调用呢?下文会详细说以下如何间接调用虚函数的。
非虚函数: 非虚函数在编译器就已经静态确定,地址也是静态确定的, 一个子类 如果继承他的父类非虚函数,那么他们其实共享一个非虚函数地址。
假如存在一个非虚函数NonVirtualFunc(), 那么Base::NonVirtualFunc()和Derived::NonVirtualFunc() 具有一个相同的函数地址, 这里的函数地址可以利用调试器在调试阶段可以看出来, 是相同的。子类在调用这些非虚函数和虚函数,都经历了怎样的过程?
一个子类的成员函数在被调用时,都会默认传入一个参数this. 这个this 关键字 区别与函数地址, 他代表的是实例地址,一个类中的非静态变量、虚函数表都是通过this关键字进行寻址的。
非虚函数的地址对编译期来说“静态”的,也就是函数地址在编译期就已经确定了,实例地址对于非虚函数只是那个 this 指针参数。
虚函数的地址,是先到实例的地址this前面去查找它的虚函数表所在的地址。然后从虚函数表里取出该函数所对应的元素(虚函数表是一个函数指针数组)来call的。(当然一个已知的类的虚函数表的内容 也是编译期静态的,但不同类的虚函数表内容不同,即运行时多态的基础)
this 指针
Q1:什么情况下可以使用this指针?
this只能在成员函数中使用。全局函数,静态函数都不能使用this。实际上,成员函数默认第一个参数为T* const register this。
为什么this指针不能在静态函数中使用?
大家可以这样理解,静态函数如同静态变量一样,他不属于具体的哪一个对象,静态函数表示了整个类范围意义上的信息,而this指针却实实在在的对应一个对象,所以this指针当然不能被静态函数使用了。
静态函数只能调用静态函数和使用静态变量,但是this指针所指向的变量是非静态的,所以静态函数中使用this指针不合理也不合法。Q2:
问题解释
void testprint(const char* c) { Base::PrintWord(c); }
对于上述的函数调用来实现多态,我们就尝试用以上的知识来解释一下:
对于非虚函数
PrintWord()
, 子类在继承时保持了该函数的函数地址不变, 子类在调用Base::PrintWord()
时, 相当于调用了Derived::PrintWord()
。由于一个类的非静态成员函数在调用非静态成员函数时,会将this指针作为参数默认的传入到函数中, 那么该调用过程就类似于
Derived::PrintWord(this, c);
- 所以在
Base::PrintWord(this, c)
中调用虚函数doPrintWord()
,其在虚函数表中寻址是基于当前this指针进行寻址的,而this指针当前指向的是子类对象,那么最终执行的虚函数是子类重载的虚函数doPrintWord()
,最终实现了多态。
写到这,我觉得公司原有的代码虽然没有问题,实现了多态,但是这样的写法确实让人感到迷惑,不如直接调用子类继承的非虚函数来的直接。
-
C++中关于 虚函数、虚析构、虚继承和虚基类
2018-04-01 18:48:28在很多次面试中,我发现面试官都喜欢问同一个问题:谈谈你对多态的认识。 遇到这个问题,通常我第一句话就是:多态分为静态多态和动态多态,然后静态多态就是balabala动态多态是巴拉巴拉。关于静态多态这里不再赘述...在很多次面试中,我发现面试官都喜欢问同一个问题:谈谈你对多态的认识。
遇到这个问题,通常我第一句话就是:多态分为静态多态和动态多态,然后静态多态就是balabala动态多态是巴拉巴拉。关于静态多态这里不再赘述。这里先描述一下博主在CVTE面试时遇到的多态问题。面试官要求:你先写一个多态的例子,然后我就飞快的写下了下面的语句:class Animal { public: virtual void eat() { cout << "Animal eat" << endl; } }; class Dog : public Animal { private: void eat() { cout << "dog eat" << endl; } }; int main() { Animal *A = new Dog; A->eat(); return 0; }
然后面试官接着问,这个类Dog中的函数eat为什么要把属性设置为public,设置为private行不行呢,如果设置为private,基类指针是否还可以访问这里的eat函数?当时遇到这个问题有点懵,因为接口函数都是用来被类的对象调用的,一般就直接设置为public了,怎么会想到设为私有呢。后来自己回来想清楚了,面试官这样问其实就是考察你对虚函数原理是否了解。
先公布一下答案:答案是可以的。下面说说原因。类的内存结构
如果一个类中有虚函数,类的起始位置将存放虚函数表的地址(4个字节),虚函数表是按顺序存放该类的虚函数。虚函数表地址之后再存放该类的数据成员。在无继承的情况下,不管虚函数有多少个,系统为其分配的内存空间只有4个字节。这样,我们就知道用sizeof关键字计算类的大小。虚函数表
多态与成员函数的访问权限是没有关系的, 即是两回事。
现在来解释一下为什么可以突破对象访问私有函数的限制:编译器编译是静态的,运行时多态是动态的,也就是说编译器在编译的时候不太关注动态特性。A的指向Animal类型对象的指针,编译器编译时将A当成指向Animal类型对象处理,Animal中恰好有个成员函数是eat(),并且Animal中的eat()函数属性是public,所以A->eat()语句是合法的,编译时可以通过编译器的检查,但程序在执行时通过虚函数表动态匹配调用的虚函数,所以我们看到的执行结果是“dog eat”基类定义了虚函数, 并且是public的,那么子类只要override 虚函数 无论放在什么样的访问权限下(private,protect,public), 都以基类的访问权限为准, 即是public的。
关于虚函数的特点:
(1)要有子类公有继承父类,虚函数才有意义
(2)子类重写的父类的虚函数也是虚函数(类Dog中的eat函数是虚函数),只不过省略了关键字virtual
(3)虚函数必须是所在类的成员函数,而不能是友元函数或静态函数。因为虚函数调用要靠特定的对象来决定激活哪一个函数。
(4)构造函数不能是虚函数,原因是构造函数在创建时首先被调用,也就是说在编译时就要把构造函数的地址绑定好。而析构函数可以是虚函数,因为析构函数最后调用,可以在运行时动态匹配。虚析构
把析构函数设为虚函数主要是为了防止内存泄漏
我们首先要知道的事实(单继承时):
构造函数的调用顺序:从当前类往上找父类,一直找到最上层的父类,我们可以理解为始祖,它是最先构造的,然后沿着继承路径依次往下构造,一直到当前类。
析构函数的调用顺序:从当前类开始析构,析构完再沿着继承路径往上找父类 ,析构父类 ,再找到最上层的父类 析构。分析以下代码:
#include <iostream> using namespace std; class A { public: A() { cout << "A的构造函数" << endl; } ~A() { cout << "A的析构函数" << endl; } }; class B:public A { public: B() { p = new char[20]; cout << "B的构造函数" << endl; } ~B() { if (p != NULL) delete[] p; cout << "B的析构函数" << endl; } private: char *p; }; void func(A *pa) { delete pa; } int main() { A *pa = new B; //构造函数被调用两次,A先B后 func(pa); //析构函数只被调用一次,A调用 return 0; }
执行结果:
在main函数中用new建立一个子类无名对象和定义一个父类对象指针,并将匿名对象的地址赋给父类对象指针,当我们用delete运算符回收匿名对象时,系统只执行父类的析构函数,而不执行子类的析构函数。
解决办法:把基类的析构函数定义为虚函数关于这类问题,博主心血来潮,还想赘述一番,于是又举了个栗子:
(1)不是虚析构#include <iostream> using namespace std; class A { public: ~A() //(1)基类A的析构未加virtual,即不是虚析构 { cout << "A的析构" << endl; } }; class B : public A { public: ~B() { cout << "B的析构" << endl; } }; int main() { A *pa1 = new A; delete pa1; //A的析构 A *pa2 = new B; delete pa2; //A的析构 B *pb = new B; delete pb; //B的析构 A的析构 return 0; }
(2)是虚析构
#include <iostream> using namespace std; class A { public: virtual ~A() //(2)基类A的析构加virtual,即是虚析构 { cout << "A的析构" << endl; } }; class B : public A { public: ~B() { cout << "B的析构" << endl; } }; int main() { A *pa1 = new A; delete pa1; //A的析构 A *pa2 = new B; delete pa2; //B的析构 A的析构 B *pb = new B; delete pb; //B的析构 A的析构 return 0; }
还有一个可能会被误导的地方:虚函数的默认参数是动态还是静态
看下面的栗子:#include <iostream> using namespace std; class A { public: virtual void fun(int i = 10) { cout << "A::fun() is calling, i = " << i << endl; } }; class B:public A { public: virtual void fun(int i = 99) { cout << "B::fun() is calling, i = " << i << endl; } }; int main() { A *pa = new B; pa->fun(); return 0; }
相信很多朋友觉得结果为“B::fun() is calling, i = 99”更符合预期。其实这是C++的虚函数规则,虚函数是动态绑定的,而虚函数的默认参数是静态绑定的,因此在编译时已经把i的值确定为10,而不管在后面执行时动态调用哪个虚函数。多重继承
一个类有多个直接基类的继承关系称为多继承class D : public B, public A, public C { };
多继承时的构造函数调用顺序:按照它们被继承时声明的顺序(从左到右)(B A C)
钻石型继承二义性:
如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时,可能产生二义性。虚继承 和 虚基类
虚继承主要用来解决多继承时可能发生对同一父类继承多次而产生的二义性问题。为最远的子类提供唯一的父类成员,而不重复产生多次复制。多个子类继承自同一个父类时,将继承方式指定为虚继承(继承时加关键字virtual)即可,此时的父类称为虚基类。class A{}; class B : virtual public A{}; class C : virtual public A{}; class D : public C, public B { };
这里的A就是虚基类。
-
关于C++多重继承的时候虚函数覆盖的问题
2015-06-14 13:42:30copy代码, g++编译,观察输出结果,会发现,在派生类@Derived 中重新定义了虚函数 virtual void f(void) 这样以来,基类的函数实现就会被覆盖。通过虚表调用函数f,都会调用重新定义的这个,而不是旧的(实质是指针...问题描述:
下面的代码试图去通过运行时程序分析和验证C++的虚表实现机制。
下面的代码在探究多重继承情况下,虚表的情况
copy代码, g++编译,观察输出结果,会发现,在派生类@Derived 中重新定义了虚函数 virtual void f(void)
这样以来,基类的函数实现就会被覆盖。通过虚表调用函数f,都会调用重新定义的这个,而不是旧的(实质是指针的覆盖)。
但是在我测试的时候出现了一个很奇葩的问题
观察输出,你会看到这里Derived::f()的函数实现的地址,居然有三个不同的地址,我都惊呆了。。。
理论上,实例化一个Derived::f()函数就可以了。把这个实例化的函数首地址指针覆盖到对应的虚表中即可。
但是这里居然出现了三个不同的Derived::f() (最起码看起来是不一样的。他们的地址不同额。。。)
表示很困惑。。。
为什么居然会有三个不同的Derived::f(),故意的嘛?
/******************************************************************* Programmer : EOF Date : 2015.06.13 File : virtual_function_for_multiple_inheritance.cpp E-mail : jasonleaster@gmail.com ******************************************************************/ #include <iostream> using namespace std; typedef void (*FUN) (void); class Base_1 { public: int num; Base_1():prv_data(100), num(100) { } virtual void f(void){ cout << " Base_1::f()" << endl; } virtual void g(void){ cout << " Base_1::g()" << endl; } virtual void h(void){ cout << " Base_1::h()" << endl; } private: /* Private Data */ int prv_data; }; class Base_2 { public: int num; Base_2():prv_data(200), num(200) { } virtual void f(void){ cout << " Base_2::f()" << endl; } virtual void x(void){ cout << " Base_2::x()" << endl; } private: /* Private Data */ int prv_data; }; class Base_3 { public: int num; Base_3():prv_data(300), num(300) { } virtual void g(void){ cout << " Base_3::g()" << endl; } private: /* Private Data */ int prv_data; }; class Derived : public Base_3, public Base_2, public Base_1 { public: int num; Derived():num(42), prv_data(42) { } virtual void y(void){ cout << " Derived::y()" << endl; } /* Here we re-implement the virtual function @f(). Compiler will rewrite the virtual table */ virtual void f(void){ cout << " Derived::f()" << endl; } virtual void g(void){ cout << " Derived::g()" << endl; } private: int prv_data; }; template<class T> unsigned long* get_element(T &obj, int offset = 0, int vprt_offset = 0) { return ((unsigned long*)*((unsigned long*)(&obj) + vprt_offset)) + offset ; } int main() { Derived d; cout << "Virtual Table of @Base_3 and @Derived:" << endl; cout << (int*)*get_element(d, 0, 0) << endl; ((FUN)(*get_element(d, 0, 0))) (); cout << (int*)*get_element(d, 1, 0) << endl; ((FUN)(*get_element(d, 1, 0))) (); cout << (int*)*get_element(d, 2, 0) << endl; ((FUN)(*get_element(d, 2, 0))) (); cout << "Virtual Table of @Base_2:" << endl; cout << (int*)*get_element(d, 0, sizeof(Base_3)/8) << endl; ((FUN)(*get_element(d, 0, sizeof(Base_3)/8))) (); cout << (int*)*get_element(d, 1, sizeof(Base_3)/8) << endl; ((FUN)(*get_element(d, 1, sizeof(Base_3)/8))) (); cout << "Virtual Table of @Base_1:" << endl; cout << (int*)*get_element(d, 0, (sizeof(Base_3) + sizeof(Base_2))/8) << endl; ((FUN)(*get_element(d, 0, (sizeof(Base_3) + sizeof(Base_2))/8))) (); cout << (int*)*get_element(d, 1, (sizeof(Base_3) + sizeof(Base_2))/8) << endl; ((FUN)(*get_element(d, 1, (sizeof(Base_3) + sizeof(Base_2))/8))) (); cout << (int*)*get_element(d, 2, (sizeof(Base_3) + sizeof(Base_2))/8) << endl; ((FUN)(*get_element(d, 2, (sizeof(Base_3) + sizeof(Base_2))/8))) (); return 0; }
-
Java语言的虚函数是怎么样的?
2017-01-20 17:11:26以下关于面向对象的描述错误的是: 面向对象的基本特性是封装,继承和多态 构造函数不可以是虚函数,析构函数可以是虚函数 子类重新定义父类虚函数的方法叫做重载 多态是为了接口重用,封装和继承是为了代码... -
关于多重继承中覆盖虚基类的函数问题--《C++程序设计语言》
2012-06-09 16:00:36当且仅当某个覆盖类是从覆盖此函数的每个类派生出时,才允许这样做。也就是说,必须有一个函数覆盖了所有其他的覆盖函数。 2.如果两个类覆盖了同一个基类函数,但它们互不覆盖,这个类层次结构就是错的。 这两... -
171228—虚继承&虚基类、虚函数、纯虚函数&抽象类 这一家人
2017-12-29 11:24:11虚继承&虚基类定义:虚继承:在继承定义中包含了virtual关键字的继承关系,它描述了一种无关于公有、私有、保护继承的继承方式。 虚基类:在虚继承体系中的通过virtual继承而来的基类,即被虚继承的基类。 但是!... -
二级c++考试之虚函数与纯虚函数,抽象类
2020-04-11 12:04:29Class parent { public: virtual void Name()=0; }; class child_1: public parent ...关于上述类定义,下列描述中错误的是______。 A.类Koala是类Animal的派生类 B.类Koala中的Name函数是一个虚函数 C.类Anim... -
C++常见面试题(三)——虚函数表详解
2020-05-17 17:36:09C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。... -
C++子类 父类的相互转换 和 虚函数
2013-12-25 13:22:58今天在程序中遇到一个问题,关于子类 父类的强制转换的。查了下网络,大概弄懂了些,记录下来作为笔记。 先看一个例子 【引自雁南飞的博客】在C++的世界中有这样两个概念,向上类型转换,向下类型转换,... -
C++入门之初话多态与虚函数
2019-01-20 11:46:00多态性是面向对象程序设计的又一个重要思想,关于多态的详尽描述,请看本人的收藏https://www.cnblogs.com/hust-ghtao/p/3512461.html。这篇博文中,详尽的探讨了多态的一些特性。 在此,我仅仅以白话的方式描述... -
带有虚表的类的内存分布总结
2019-10-06 14:41:30这个问题一直似是而非的,昨天闲着无事...5、关于虚析构的描述 6、关于纯虚函数为何必须在子类中实现的问题。 未完成部分: 1、关于虚基类的结构分布。 1、空类的内存分布比较简单,一般用一个字节表示,据说... -
读书笔记 effective c++ Item 7 在多态基类中将析构函数声明为虚析构函数
2017-02-13 19:01:001. 继承体系中关于对象释放遇到的问题描述 1.1 手动释放 关于时间记录有很多种方法,因此为不同的计时方法创建一个TimeKeeper基类和一些派生类就再合理不过了: 1 class TimeKeeper { 2 3 public: 4 5 ... -
面向对象的相关描述
2017-07-08 20:20:15子类重新定义父类虚函数的方法叫做重载 多态是为了接口重用,封装和继承是为了代码重用 解析: 对于C++中经常出现的函数名称相同但是参数列表或者返回值不同的函数,主要存在三种情况: 1. -
关于多态的讨论
2015-03-28 12:30:34首先来看看多态的定义,这里引用Charlie Calverts对多态的描述——多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作... -
VS中C++对象的内存布局
2019-09-26 22:06:07本文主要简述一下在Visual Studio中C++对象的内存布局,这里没有什么测试代码,只是以图文的形式来描述一下内存分布,关于测试的代码以及C++对象模型的其他内容大家可以参考一下陈皓先生的几篇博文以及网上的其他... -
最权威的C++教程_C++_Primer_Plus中文第五版+C++_Primer中文第四版(都含源码+习题)(共4分卷)分卷1
2010-06-23 17:33:55虚函数、动态内存分配、继承、代码重用、友元、异常处理技术、string类和标准模板库、输入/输出等内 容。 本书针对C++初学者,从C语言基础知识开始介绍,然后在此基础上详细阐述C++新增的特性,因此不要 求读者... -
最权威的C++教程_C++_Primer_Plus中文第五版+C++_Primer中文第四版(都含源码+习题)(共4分卷)分卷2
2010-06-23 17:47:19虚函数、动态内存分配、继承、代码重用、友元、异常处理技术、string类和标准模板库、输入/输出等内 容。 本书针对C++初学者,从C语言基础知识开始介绍,然后在此基础上详细阐述C++新增的特性,因此不要 求读者... -
最权威的C++教程_C++_Primer_Plus中文第五版+C++_Primer中文第四版(都含源码+习题)(共4分卷)分卷3
2010-06-23 18:03:39虚函数、动态内存分配、继承、代码重用、友元、异常处理技术、string类和标准模板库、输入/输出等内 容。 本书针对C++初学者,从C语言基础知识开始介绍,然后在此基础上详细阐述C++新增的特性,因此不要 求读者... -
C++网络作业答案.docx
2020-09-29 14:15:43下列关于动态联编的描述中错误的是 A动态联编是以虚函数为基础的 B动态联编是在运行时确定所调用的函数代码的 C动态联编调用函数操作是指向对象的指针或对象引用 D动态联编是在编译时确定操作函数的 注先期联编也称... -
c++选择题(选择题)
2020-07-07 08:37:21关于纯虚函数和抽象类的描述中,(C )是错误的。 A.纯虚函数是一种特殊的虚函数,它没有具体的实现 B.抽象类是指具有纯虚函数的类 C.一个基类中说明有纯虚函数,该基类的派生类一定不再是抽象类 D.抽象类只能... -
c++网络作业6答案.pdf
2020-05-13 06:17:37选择题 1 下列关于动态联编的描述中错误的是_D A)动态联编是以虚函数为基础的 B)动态联编是在运行时确定所调用的函数代码的 C)动态联编调用函数操作是指向对象的指针或对象引用 D)动态联编是在编译时确定操作函数的 ... -
百度垂直搜索部门实习面经
2012-08-01 15:43:26之前面了一次百度的实习,并且拿到offer,下面是实习过程中出现的题目,希望给同样投百度的同学一个参考。 百度垂直搜索部门实习面经 ...1. 背景问题:自我介绍,问...2. 构造函数能否是虚函数(我说的比较含糊 -
《C++ 程序设计》期末考试及答案.pdf
2020-08-10 20:35:16下列关于虚基类的描述错误的是 期末考试试卷 A. 设置虚基类的目的是为了消除二义性 B. 虚基类的构造函数在非虚基类之后调用 C. 若同一层中包含多个虚基类这些虚基类的构造函数按它们说明的次序调用 D. 若 -
多态性(小题)
2020-06-27 16:52:35判断题: 1-1 ...关于动态绑定的下列描述中,( D )是错误的。 A.动态绑定是以虚函数为基础的 B.动态绑定在运行时确定所调用的函数代码 C.动态绑定调用函数操作是通过指向对象的指针或对象引用来实. -
《C++程序设计》期末考试与答案.doc
2020-04-27 16:42:56int *p=new int[40](0) 下列关于虚基类的描述错误的是 期末考试试卷 A. 设置虚基类的目的是为了消除二义性 B. 虚基类的构造函数在非虚基类之后调用 C. 若同一层中包含多个虚基类这些虚基类的构造函数按它们说明的... -
《C++程序设计》期末考试与答案.pdf
2020-04-27 13:34:06下列关于虚基类的描述错误的是 期末考试试卷 A. 设置虚基类的目的是为了消除二义性 B. 虚基类的构造函数在非虚基类之后调用 C. 若同一层中包含多个虚基类这些虚基类的构造函数按它们说明的次序调用 考