执行如下代码,会输出 钢琴:叮咚叮咚 和 吉他:咚咚当当 。这体现了面向对象编程的( )特性。
class Instrument {
public:
virtual void play() {
cout << "乐器在演奏声音" << endl;
}
virtual ~Instrument() {}
};
class Piano : public Instrument {
public:
void play() override {
cout << "钢琴: 叮咚叮咚" << endl;
}
};
class Guitar : public Instrument {
public:
void play() override {
cout << "吉他: 咚咚当当" << endl;
}
};
int main() {
Instrument* instruments[2];
instruments[0] = new Piano();
instruments[1] = new Guitar();
for (int i = 0; i < 2; ++i) {
instruments[i]->play();
}
for (int i = 0; i < 3; ++i) {
delete instruments[i];
}
return 0;
}分析时间复杂度时关注循环层数和每层迭代次数,递归算法则分析递归树。注意最坏/最好/平均情况的区别。
分析复杂度时注意外层循环和内层循环的关联。例如内外两层都到n的循环不一定是O(n²),要看内层是否每次都在缩小。
【考点】多态|虚函数|动态绑定
1. 🎯 考纲定位 本题考查 GESP 六级《面向对象编程》模块核心考点——多态。命题人专门用这段代码卡住大量只背名词的考生:他们知道"多态"这个词,却分不清"继承"和"多态"在代码里的区别,更不懂"虚函数"才是实现多态的关键标志。
2. 🧠 解题思维 解题链要抓住三个关键点:第一,基类 Instrument 的 play 函数加了 virtual 关键字;第二,派生类 Piano/Guitar 都用了 override 重写 play;第三,main 函数里用的是 Instrument* 指针数组。看到 virtual 就应联想到"动态绑定",看到派生类重写就应联想到"运行时多态"——这才是 C++ 多态的完整实现条件。
3. 🪤 陷阱拆解 陷阱一:选项 A 继承是概念陷阱。继承只建立类之间的关系,而本题输出的是不同乐器声音,说明在运行时刻"多态"起了作用。陷阱二:选项 B 封装是概念陷阱。封装强调隐藏内部细节,而本题恰恰暴露了 play 的具体实现。真正阴险的是代码里两个细节:用基类指针存储派生对象(如果不用 virtual 就会调用基类版本),以及循环中调用 play(如果没 virtual 就会调用基类版本)。
4. 🔄 反命题思维 反命题方向:如果把 instruments 改成 Piano* 数组,答案还是 C 多态;如果把 main 里循环改成 for (auto p : instruments) p->play(); 答案不变;如果去掉 virtual 关键字,答案会变成 A 继承(因为所有输出都变成基类版本)。刷到这类题,先问自己"是不是基类指针调了重写函数"。