C++ 继承方式详解
在C++中,继承是一种重要的面向对象编程技术,允许一个类(派生类)继承另一个类(基类)的属性和方法。C++提供了四种主要的继承方式:public、protected、private以及虚继承(virtual inheritance)。
派生类会继承基类中定义的所有成员,但是否能访问这些成员取决于继承的方式。例如,public继承使得基类的公有成员在派生类中仍保持公有状态;而private继承则会使基类的所有成员在派生类中变为私有。
虚继承: 虚继承主要用于解决多重继承中可能出现的菱形问题,即当多个基类共同继承同一个祖先类时,为了避免在派生类中多次实例化同一个基类的问题,可以使用虚继承。例如,假设有一个类A,两个类B1和B2分别继承自A,而类D同时继承自B1和B2。如果不使用虚继承,D中将存在两个独立的A类实例。通过将B1和B2对A的继承设为虚继承,可以确保D中只有一个A类的实例。
class A {}; // 基类
class B1 : public virtual A {}; // 虚继承
class B2 : public virtual A {}; // 虚继承
class D : public B1, public B2 {}; // 多重继承
虽然虚继承有助于避免冗余,但在实际开发中较少使用,因为它会增加程序的复杂性和运行时的开销。
虚继承下的函数调用机制
在涉及虚继承的情况下,如何确保通过基类指针调用派生类中特定的重写方法是一个常见的面试问题。例如,如果有类B和C虚继承自A,而D同时继承自B和C,那么如何确保通过A*指针调用的是B中重写的虚函数?
#include
using namespace std;
class A {
public:
virtual void fun() { cout <<"A::fun()." <};
class B : public virtual A {
public:
void fun() override { cout <<"B::fun()." <};
class C : public virtual A {
public:
void fun() override { cout <<"C::fun()." <};
class D : public B, public C {};
int main() {
A* a = new D;
A* b = new B;
a = b;
a->fun();
return 0;
}
上述代码中,通过将a指针指向B类的实例,确保调用了B中重写的虚函数。
多态性解析
多态性是面向对象编程的一个核心概念,它允许不同类的对象通过相同的接口进行操作。C++中的多态性分为静态多态性和动态多态性。
- 静态多态性(静态联编): 由编译器在编译时确定调用的具体函数,通常通过函数重载实现。
- 动态多态性(动态联编): 由运行时环境根据对象的实际类型确定调用的具体函数,通常通过虚函数实现。
例如,静态多态性可以通过函数重载实现:
int Add(int left, int right) {
return left + right;
}
float Add(float left, float right) {
return left + right;
}
int main() {
cout < cout < return 0;
}
而动态多态性则通过虚函数实现:
class Base {
public:
virtual void show() { cout <<"Base show." <};
class Derived : public Base {
public:
void show() override { cout <<"Derived show." <};
int main() {
Base* basePtr = new Derived();
basePtr->show(); // 动态绑定,调用Derived::show()
delete basePtr;
return 0;
}
动态多态性的实现依赖于虚函数表(vtable)和虚函数指针(vptr),这些机制确保了运行时的正确函数调用。
引用与指针的使用场景
在C++中,引用和指针都是用来间接访问对象的工具,但它们在使用场景上有所不同。
- 使用引用的主要场景:
- 当需要修改调用函数中的数据对象时。
- 通过传递引用而不是整个对象,可以提高程序的运行效率。
- 使用指针的主要场景:
- 当需要管理动态分配的内存时。
- 当需要表示“没有对象”(即空指针)的情况时。
具体选择使用引用还是指针,还需要考虑以下因素:
- 如果数据对象较小,如基本数据类型或小型结构体,建议按值传递。
- 如果数据对象较大,如大型结构体或类对象,建议使用引用或指针以提高效率。
- 如果需要修改数据对象,建议使用引用。
- 如果需要表示“没有对象”的情况,建议使用指针。