C++ 多态
题目
- 关于虚函数说法正确的是( )
A.被virtual修饰的函数称为虚函数
B.虚函数的作用是用来实现多态
C.虚函数在类中声明和类外定义时候,都必须加虚拟关键字
D.静态虚成员函数没有this指针
- B
A.只有成员函数才能加 virtual,全局函数不允许加 virtual
B.正确
C.virtual关键字只在声明时加上,在类外实现时不能加
D.static和virtual是不能同时使用的
为什么静态函数不能是虚函数? 因为虚函数的调用机制要求对象实例,而静态函数完全绕过了对象实例。
-
虚函数需要对象的 vptr 来查找虚表中的函数地址 而静态函数没有对象 → 没有 vptr → 无法访问虚函数表。
-
静态函数没有 this 指针 虚函数几乎总是通过 this 指针进行行为差异(因为它基于对象类型)
没有 this 就没有虚函数的意义
- 编译器不支持,语法上直接报错
- 关于不能设置成虚函数的说法正确的是( )
A.友元函数可以作为虚函数,因为友元函数出现在类中
B.成员函数都可以设置为虚函数
C.静态成员函数不能设置成虚函数,因为静态成员函数不能被重写
D.析构函数建议设置成虚函数,因为有时可能利用多态方式通过基类指针调用子类析构函数
- D
A.友元函数不属于成员函数,不能成为虚函数
B.静态成员函数就不能设置为虚函数
C.静态成员函数与具体对象无关,属于整个类,核心关键是没有隐藏的this指针,可以通过类名::成员函数名 直接调用,此时没有this无法拿到虚表,就无法实现多态,因此不能设置为虚函数
D.尤其是父类的析构函数强力建议设置为虚函数,这样动态释放父类指针所指的子类对象时,能够达到析构的多态
- 关于重载、重写和重定义的区别说法正确的是( )【不定项选择】
A.重写和重定义都发生在继承体系中
B.重载既可以在一个类中,也可以在继承体系中
C.它们都要求原型相同
D.重写就是重定义
E.重定义就是重写
F.重写比重定义条件更严格
G.以上说法全错误
- A、F
A.重写即覆盖,针对多态, 重定义即隐藏, 两者都发生在继承体系中
B.重载只能在一个范围内,不能在不同的类里
C.只有重写要求原型相同
D.重写和重定义是两码事,重写即覆盖,针对多态, 重定义即隐藏
E.重写和重定义是两码事,重写即覆盖,针对多态, 重定义即隐藏
F.重写要求函数完全相同,重定义只需函数名相同即可
G.很明显有说法正确的答案
- 关于重载和多态正确的是 ( )
A.如果父类和子类都有相同的方法,参数个数不同,将子类对象赋给父类对象后,采用父类对象调用该同名方法时,实际调用的是子类的方法
B.选项全部都不正确
C.重载和多态在C++面向对象编程中经常用到的方法,都只在实现子类的方法时才会使用
D.class A{ public: void test(float a) { cout << a; } }; class B :public A{ public: void test(int b){ cout << b; } }; void main() { A *a = new A; B *b = new B; a = b; a->test(1.1); } 结果是1
- B
A.
参数个数不同 → 属于 函数重载,不是重写(override)
在 C++ 中,子类的同名函数会隐藏(hide)父类的同名函数,即使参数不同
并且当你用 父类对象或指针调用该函数时,只会调用父类的版本
除非父类函数是 virtual 且子类函数签名完全一致,才会触发多态(重写)
D.
分析:
A::test(float) 是定义在基类的函数
B::test(int) 是子类中的新函数,并不是重写父类的函数(参数类型不同)
但!B::test(int) 会隐藏 A::test(float)(名字相同即隐藏,无视参数列表)
然后:
a 是 A* 类型,指向 B 对象
调用 a->test(1.1),编译期只查 A 中的成员
test(float) 存在于 A,参数 1.1 是 double,可转换成 float,合法匹配
最终调用的是 A::test(float),输出是 1.1(或近似浮点)
- 假设A为抽象类,下列声明( )是正确的
A.A fun(int);
B.A*p;
C.int fun(A);
D.A obj;
- B
A. A fun(int); 这声明了一个函数 fun,返回类型是 A。
错误:返回类型是抽象类 A,意味着需要返回一个 A 类型的值,这会尝试调用复制构造函数,不允许返回抽象类对象
B. A p;*
正确:抽象类可以作为指针类型声明,因为不涉及构造对象。
通常用于实现多态:A* p = new Derived;
C. int fun(A);
正确:你可以定义一个参数类型为抽象类 A 的函数,尽管你不能传值构造一个 A。
常用于接口设计,尽管实参必须是 A 的子类对象(并传引用更合理)
D. A obj;
错误:抽象类不能定义对象,直接实例化会报错
- 关于虚表说法正确的是( )
A.一个类只能有一张虚表
B.基类中有虚函数,如果子类中没有重写基类的虚函数,此时子类与基类共用同一张虚表
C.虚表是在运行期间动态生成的
D.一个类的不同对象共享该类的虚表
- D
C.虚表是在编译期间生成的
- 下面函数输出结果是( )
A.B::f()
B.A::f(),因为子类的f()函数是私有的
C.A::f(),因为强制类型转化后,生成一个基类的临时对象,pa实际指向的是一个基类的临时对象
D.编译错误,私有的成员函数不能在类外调用
class A
{
public:
virtual void f()
{
cout<<"A::f()"<<endl;
}
};
class B : public A
{
private:
virtual void f()
{
cout<<"B::f()"<<endl;
}
};
A* pa = (A*)new B;
pa->f();
- A
在 C++ 中,访问控制符(private / protected / public)只约束 "谁能写出调用代码"(即编译期能否访问),而虚函数分发(调用哪个函数实现)发生在运行时,并不受它是否是 private 的影响。
- 语法阶段:
pa 是 A* 类型,调用 f() 是合法的(A::f() 是 public)
所以:编译器不会报错
- 运行时阶段:
pa 实际指向的是 B 对象
所以查虚函数表,调用 B::f()
即使 B::f() 是 private,编译器不会重新检查访问权限,因为你是通过 A 的接口调用的,访问权限检查已经过去了
学习过程中的一些记录